﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel;

using Noris.Schedule.Support;
using Noris.Schedule.Support.Services;
using Noris.Schedule.Support.Data;

namespace Noris.Schedule.UI.Components
{
	#region CLASS DColumns : sada sloupců
	/// <summary>
	/// DColumns : sada sloupců (vizuální a datová vrstva)
	/// </summary>
    public class DColumns : DblGraphControl, IHeaderSync
	{
		#region KONSTRUKCE
		public DColumns()
		{
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);
			this.Columns = new List<DColumn>();
            this.ColumnIdx = new Dictionary<string, int>();
            this.SkipEvents = true;
            this.ColumnHeaderHeightSizable = true;
			// this.Paint += new PaintEventHandler(DColumns_Paint);
            this.MouseMove += new MouseEventHandler(_MouseMove);
            this.MouseDown += new MouseEventHandler(_MouseDown);
            this.MouseUp += new MouseEventHandler(_MouseUp);
            this.MouseLeave += new EventHandler(_MouseLeave);
            this.SizeChanged += new EventHandler(_SizeChanged);
            this.TabStop = false;
            this.TabStopChanged += new EventHandler(_TabStopChanged);
            this._ColumnAddSystem();
            this.SkipEvents = false;
            this._CurrentCursorType = CursorType.Default;

            this._ComponentsInitialized = true;
        }
        /// <summary>
        /// Změnila se velikost this
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _SizeChanged(object sender, EventArgs e)
        {
            this.RecalculateColumnWidthToFill();

			// Akceptovat výšku zadanou explicitně, do this proměnných: to není tak triviální, vysvětlíme:
			// a) Pokud měním výšku interaktivně myší, 
			//     - pak se aktuální výška vepisuje do ColumnHeaderHeightInteractive a odsud do Height.
			//     - ale nezapisuje se do ColumnHeaderHeight
			//    Při zápisu do Height se vyvolá event _SizeChanged (zdejší) a zjistím, že Height == ColumnHeaderHeightInteractive, pak nic dalšího nedělám!
			// b) Pokud měním výšku aplikací, tak že nastavuji this.ColumnHeaderHeight, pak se rovněž vyvolá this event
			//   Jakmile ale výška this.Height odpovídá alespoň jedné hodnotě ColumnHeaderHeight nebo ColumnHeaderHeightInteractive, 
			//    pak nemohu spustit přiřazení ani do jedné property, došlo by k cyklickému volání.
			// c) Musím tedy ošetřit pouze ten stav, kdy někdo mění this.Height (anebo Size nebo Bounds) bez využití ColumnHeaderHeight...,
			//   Provedu to tak, že aktuální výšku zapíšu do ColumnHeaderHeight, tam (v set) se hodnota přepíše do ColumnHeaderHeightInteractive:
			if (this.Height != this.ColumnHeaderHeightInteractive && this.Height != this.ColumnHeaderHeight)
				this.ColumnHeaderHeight = this.Height;
		}
        void _TabStopChanged(object sender, EventArgs e)
        {
            this.TabStop = false;
        }
        /// <summary>
        /// Sloupce. Pořadí se nikdy nemění. Sloupce lze vyjmout a vložit.
        /// Na pozici [0] je vždy systémový sloupec. První datový je na pozici [1], má ale logický index [0].
        /// </summary>
        protected List<DColumn> Columns;
		/// <summary>
		/// Přístup ke sloupci podle jeho nativního indexu (v pořadí, v jakém se vkládaly)
		/// </summary>
		/// <param name="idx"></param>
		/// <returns></returns>
		internal DColumn Column(int idx)
		{
			return this.Columns[idx];
		}
        /// <summary>
        /// Dictionary, která obsahuje Key = string, a Value = fyzický index (proto má v názvu "Idx"). Liší se tak od DColumn.Index, což je logický index (LOG = FYZ - 1).
        /// </summary>
		protected Dictionary<string, int> ColumnIdx;
        /// <summary>
        /// Aktuální druh kurzoru, trvalá paměť poslední hodnoty pro detekci změny
        /// </summary>
        private CursorType _CurrentCursorType;
		/// <summary>
		/// Objekt pro sycnhronizaci dat více objektů DColumns
		/// </summary>
		private DColumnsSynchronizer _Synchronizer;
        /// <summary>
        /// Pozice Top stabilní. Při interaktivní změně se zde drží výchozí hodnota Top, přepisuje se až při ukončení Move (MouseUp).
        /// </summary>
        private int _ColumnHeaderTop;
        /// <summary>
        /// Pozice Top aktuální, mění se při interaktivní změně.
        /// </summary>
        private int _ColumnHeaderTopInteractive;
		/// <summary>
		/// Výška stabilní. Při interaktivní změně se zde drží výchozí hodnota Height, přepisuje se až při ukončení Resize (MouseUp),
		/// </summary>
        private int _ColumnHeaderHeight;
		/// <summary>
		/// Výška aktuální, mění se při interaktivní změně.
		/// </summary>
        private int _ColumnHeaderHeightInteractive;
		/// <summary>
		/// Výška, pro kterou se naposledy umisťovaly interní prvky (RowFilter)
		/// </summary>
		private int _HeightLastCalculated;
        /// <summary>Příznak stavu, kdy jsou komponenty inicializovány, a mohou se vykreslovat. Před tím vykreslování neprovádíme.</summary>
        private bool _ComponentsInitialized = false;
		#endregion
        #region OBSLUHA METOD A PROPERTY ZDĚDĚNÝCH OD DblGraphControl
        /// <summary>
        /// Tuto metodu mají přepisovat potomkové, kteří chtějí reagovat na změnu velikosti.
        /// Až si připraví objekty, mají zavolat base.ResizeAfter(), kde se zajistí vyvolání Draw() => event PaintToBuffer().
        /// </summary>
        protected override void ResizeAfter()
        {
            if (!_ComponentsInitialized) return;
            // this.RecalculateSegmentArea();
            base.ResizeAfter();
        }
        /// <summary>
		/// Potlačení kreslení při provádění rozsáhlejších změn. Po ukončení je třeba nastavit na false !
        /// Default = false = kreslení není potlačeno.
        /// Při provádění rozsáhlejších změn je vhodné nastavit na true, a po dokončení změn vrátit na false => tím se automaticky vyvolá Draw.
        /// </summary>
        public override bool SuppressDrawing
        {
            get
            {
                return base.SuppressDrawing;
            }
            set
            {
                base.SuppressDrawing = value;
                this._TimeAxis.SuppressDrawing = value;
            }
        }
        /// <summary>
        /// Potlačení kreslení Headeru : promítnout i do TimeOriginal.
        /// </summary>
        public override void SuppressDrawingPush()
        {
            base.SuppressDrawingPush();
            this._TimeAxis.SuppressDrawingPush();
        }
        /// <summary>
        /// Obnovení kreslení Headeru : promítnout i do TimeOriginal.
        /// </summary>
        public override void SuppressDrawingPop()
        {
            base.SuppressDrawingPop();
            this._TimeAxis.SuppressDrawingPop();
        }
        #endregion
        #region PROPERTY
        /// <summary>
        /// Pozice Top záhlaví sloupců interaktivní (mění se v průběhu vertical move)
        /// </summary>
        [Browsable(true)]
        [Category("DColumns")]
        [Description("Pozice Top záhlaví sloupců interaktivní (mění se v průběhu vertical move)")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int ColumnHeaderTopInteractive
        {
            get { return this._ColumnHeaderTopInteractive; }
            set
            {
                int topOld = this._ColumnHeaderTop;
                int topNew = _TopIntoRange(value);
                if (topNew > 0 && topNew != topOld)
                {
                    this._ColumnHeaderTopInteractive = topNew;
                    this.OnColumnHeaderTopChanging();
                }
            }
        }
        /// <summary>
        /// Pozice Top záhlaví sloupců aktuální (trvalá, při pohybu je zde původní pozice před zahájením pohybu)
        /// </summary>
        [Browsable(true)]
        [Category("DColumns")]
        [Description("Pozice Top záhlaví sloupců aktuální (trvalá, při pohybu je zde původní pozice před zahájením pohybu)")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int ColumnHeaderTop
        {
            get { return this._ColumnHeaderTop; }
            set
            {
                int topOld = this._ColumnHeaderTop;
                int topNew = _TopIntoRange(value);
                if (topNew > 0 && topNew != topOld)
                {
                    this._ColumnHeaderTop = topNew;
                    this._ColumnHeaderTopInteractive = topNew;
                    this.OnColumnHeaderTopChanged();
                }
            }
        }
		/// <summary>
		/// Výška záhlaví sloupců interaktivní (mění se v průběhu resize)
		/// </summary>
        [Browsable(true)]
        [Category("DColumns")]
        [Description("Výška záhlaví sloupců interaktivní (mění se v průběhu resize)")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int ColumnHeaderHeightInteractive
		{
			get { return this._ColumnHeaderHeightInteractive; }
			set
			{
                int heightOld = this._ColumnHeaderHeightInteractive;
                int heightNew = _HeightIntoRange(value);
                if (heightNew > 0 && heightNew != heightOld)
				{
                    this._ColumnHeaderHeightInteractive = heightNew;
					this.OnColumnHeaderHeightChanging();
				}
			}
		}
		/// <summary>
        /// Výška záhlaví sloupců aktuální (trvalá, při resizování je zde výška před zahájením resize)
        /// </summary>
        [Browsable(true)]
        [Category("DColumns")]
        [Description("Výška záhlaví sloupců aktuální (trvalá, při resizování je zde výška před zahájením resize)")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int ColumnHeaderHeight
		{
            get { return this._ColumnHeaderHeight; }
			set
			{
                int heightOld = this._ColumnHeaderHeight;
                int heightNew = _HeightIntoRange(value);
                if (heightNew > 0 && heightNew != heightOld)
				{
                    this._ColumnHeaderHeight = heightNew;
                    this._ColumnHeaderHeightInteractive = heightNew;
                    this.OnColumnHeaderHeightChanged();
				}
			}
		}
        /// <summary>
        /// Nejmenší povolená výška ColumnHeader
        /// </summary>
        [Browsable(true)]
        [Category("DColumns")]
        [Description("Nejmenší povolená výška ColumnHeader")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public int? ColumnHeaderHeightMinimum { get; set; }
        /// <summary>
        /// Největší povolená výška ColumnHeader
        /// </summary>
        [Browsable(true)]
        [Category("DColumns")]
        [Description("Největší povolená výška ColumnHeader")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public int? ColumnHeaderHeightMaximum { get; set; }
        /// <summary>
        /// Horní limit pro hodnotu Top. Pokud je null, není omezeno. Nemůže být záporný.
        /// Pokud by se uživatel snažil posunout objekt na pozici Top menší (nahoru), než je tento limit, pak objekt zůstane na tomto limitu.
        /// </summary>
        [Browsable(true)]
        [Category("DColumns")]
        [Description("Horní limit pro hodnotu Top")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public int? ColumnHeaderTopUpperLimit { get; set; }
        /// <summary>
        /// Dolní limit pro hodnotu Top. Pokud je null, není omezeno. Může být záporný, pak se počítá od souřadnic Bottom (Parent i this)
        /// Pokud by se uživatel snažil posunout objekt na pozici Top vyšší (nahoru), než je tento limit, pak objekt zůstane na tomto limitu.
        /// </summary>
        [Browsable(true)]
        [Category("DColumns")]
        [Description("Dolní limit pro hodnotu Top")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public int? ColumnHeaderTopDownLimit { get; set; }
        /// <summary>
        /// Je možno interaktivně měnit výšku ColumnHeaderHeight?
        /// </summary>
        [Browsable(true)]
        [Category("DColumns")]
        [Description("Je možno interaktivně měnit výšku ColumnHeaderHeight?")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public bool ColumnHeaderHeightSizable { get; set; }
        /// <summary>
        /// Režim, ve kterém se pohybuje hlavička a její parent
        /// </summary>
        [Browsable(true)]
        [Category("DColumns")]
        [Description("Režim, ve kterém se pohybuje hlavička a její parent")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public DColumnsHeaderMoveMode ColumnHeaderMoveMode { get; set; }
        /// <summary>
        /// Potlačení vnitřních eventů
        /// </summary>
        [Browsable(false)]
        [Category("DColumns")]
        [Description("Potlačení vnitřních eventů")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool SkipEvents { get; set; }
        #region POMOCNÉ METODY
        /// <summary>
        /// Vrátí danou hodnotu Top zarovnanou do limitů (ColumnHeaderTopUpperLimit ÷ ColumnHeaderTopDownLimit), počítáno pro pohyb ParentTop !!!
        /// </summary>
        /// <param name="value">Požadovaná hodnota Top pro this.Parent (typicky container obsahující Header + List) v rámci jeho vyššího parenta (v němž se tato dvojice pohybuje)</param>
        /// <returns></returns>
        private int _TopParentIntoRange(int value)
        {
            // Chybné zadání => kontrola se neprovádí:
            if (this.ColumnHeaderTopUpperLimit.HasValue && this.ColumnHeaderTopDownLimit.HasValue && this.ColumnHeaderTopDownLimit.Value > 0 && this.ColumnHeaderTopUpperLimit.Value > this.ColumnHeaderTopDownLimit.Value)
                return value;

            // Horní limit (nahoře):
            if (this.ColumnHeaderTopUpperLimit.HasValue)
            {
                if (value < this.ColumnHeaderTopUpperLimit.Value)
                    return this.ColumnHeaderTopUpperLimit.Value;
            }

            // Dolní limit:
            if (this.ColumnHeaderTopDownLimit.HasValue)
            {
                if (this.ColumnHeaderTopDownLimit.Value > 0)
                {   // Kladný = měří se od Top:
                    if (value > this.ColumnHeaderTopDownLimit.Value)
                        return this.ColumnHeaderTopDownLimit.Value;
                }
                else if (this.ColumnHeaderTopDownLimit.Value < 0 && this.Parent != null)
                {   // Záporný = měří se od Bottom (vyjadřuje prostor, který má zůstat pod tímto objektem):
                    // Vstupující hodnota (value) je hodnotou, na kterou se posune this.Parent.Top z aktuální hodnoty, 
                    //   a současně se upraví this.Parent.Height tak, aby this.Parent.Bottom zůstalo na aktuální pozici.
                    // Hodnota ColumnHeaderTopDownLimit vyjadřuje prostor, který má zbýt pod Headerem (this) do this.Parent.Bottom.

                    // Určím limitní výšku this.Parent, pod kterou se nesmíme dostat:
                    int limitH = this.Bottom - this.ColumnHeaderTopDownLimit.Value;
                    // Divné, ale je to tak: this.Bottom je pozice dolního kraje DColumn (Header), a pod ní musí zbýt požadovaná rezerva (DownLimit), která je uvedená jako záporné číslo.

                    // Určím limitní pozici this.Parent.Top, pod kterou se nesmím dostat níže (jinak by výška Height klesla pod limitH):
                    int limitT = this.Parent.Bottom - limitH;

                    // A pokud se vstupující hodnota dostala dolů (větší) než limitT, vracím limitT:
                    if (value > limitT)
                        return limitT;
                }
            }
            return value;
        }
        /// <summary>
        /// Vrátí danou hodnotu Top zarovnanou do limitů (ColumnHeaderTopUpperLimit ÷ ColumnHeaderTopDownLimit)
        /// </summary>
        /// <param name="value">Požadovaná hodnota Top pro this (DColumns) v rámci jeho parenta</param>
        /// <returns></returns>
        private int _TopIntoRange(int value)
        {
            // Chybné zadání => kontrola se neprovádí:
            if (this.ColumnHeaderTopUpperLimit.HasValue && this.ColumnHeaderTopDownLimit.HasValue && this.ColumnHeaderTopDownLimit.Value > 0 && this.ColumnHeaderTopUpperLimit.Value > this.ColumnHeaderTopDownLimit.Value)
                return value;

            // Horní limit (nahoře):
            if (this.ColumnHeaderTopUpperLimit.HasValue)
            {
                if (value < this.ColumnHeaderTopUpperLimit.Value)
                    return this.ColumnHeaderTopUpperLimit.Value;
            }

            // Dolní limit:
            if (this.ColumnHeaderTopDownLimit.HasValue)
            {
                if (this.ColumnHeaderTopDownLimit.Value > 0)
                {   // Kladný = měří se od Top:
                    if (value > this.ColumnHeaderTopDownLimit.Value)
                        return this.ColumnHeaderTopDownLimit.Value;
                }
                else if (this.ColumnHeaderTopDownLimit.Value < 0 && this.Parent != null)
                {   // Záporný = měří se od Bottom (vyjadřuje prostor, který má zůstat pod tímto objektem):
                    //  limit je hodnota Top (našeho záhlaví vrámci parenta = control, po němž se záhlaví pohybuje), kterou nesmíme překročit směrem dolů:
                    int limit = this.Parent.ClientSize.Height + this.ColumnHeaderTopDownLimit.Value /* to je záporné číslo, tedy se odečte */ - this.Height;
                    if (value > limit)
                        return limit;
                }
            }
            return value;
        }
        /// <summary>
        /// Vrátí danou hodnotu výšky zarovnanou do mezí pro výšku (Minimum ÷ Maximum)
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        private int _HeightIntoRange(int value)
        {
            // Chybné zadání => kontrola se neprovádí:
            if (this.ColumnHeaderHeightMinimum.HasValue && this.ColumnHeaderHeightMaximum.HasValue && this.ColumnHeaderHeightMinimum.Value > this.ColumnHeaderHeightMaximum.Value)
                return value;

            if (this.ColumnHeaderHeightMinimum.HasValue && value < this.ColumnHeaderHeightMinimum.Value)
                return this.ColumnHeaderHeightMinimum.Value;
            if (this.ColumnHeaderHeightMaximum.HasValue && value > this.ColumnHeaderHeightMaximum.Value)
                return this.ColumnHeaderHeightMaximum.Value;

            return value;
        }
        #endregion
        #endregion
        #region DEKLARACE PUBLIC EVENTŮ
        /// <summary>
        /// Event, který bude vyvolán po změně hodnoty Text konkrétního sloupce
        /// </summary>
        [Browsable(true)]
        [Category("Action")]
        [Description("Event, který bude vyvolán po změně hodnoty Text konkrétního sloupce")]
        public event DColumnEventHandler ColumnTextChanged;
		/// <summary>
		/// Event, který bude vyvolán opakovaně v průběhu interaktivní (výhradně !) změny pozice Parent.Top + Parent.Height
		/// </summary>
		[Browsable(true)]
		[Category("Action")]
		[Description("Event, který bude vyvolán opakovaně v průběhu interaktivní (výhradně !) změny pozice Parent.Top + Parent.Height")]
		public event EventHandler ColumnHeaderParentTopMoving;
		/// <summary>
		/// Event, který bude vyvolán po dokončení interaktivní změny a pro provedení neinteraktivní změny pozice Parent.Top + Parent.Height"
		/// </summary>
		[Browsable(true)]
		[Category("Action")]
		[Description("Event, který bude vyvolán po dokončení interaktivní změny a pro provedení neinteraktivní změny pozice Parent.Top + Parent.Height")]
		public event EventHandler ColumnHeaderParentTopMoved;
        /// <summary>
        /// Event, který bude vyvolán opakovaně v průběhu interaktivní (výhradně !) změny pozice Top
        /// </summary>
        [Browsable(true)]
        [Category("Action")]
        [Description("Event, který bude vyvolán opakovaně v průběhu interaktivní (výhradně !) změny pozice Top")]
        public event EventHandler ColumnHeaderTopChanging;
        /// <summary>
        /// Event, který bude vyvolán po dokončení interaktivní (výhradně !) změny pozice Top
        /// </summary>
        [Browsable(true)]
        [Category("Action")]
        [Description("Event, který bude vyvolán po dokončení interaktivní (výhradně !) změny a při každé neinteraktivní změně pozice Top")]
        public event EventHandler ColumnHeaderTopChanged;
		/// <summary>
		/// Event, který bude vyvolán opakovaně v průběhu interaktivní (výhradně !) změny výšky záhlaví
		/// </summary>
		[Browsable(true)]
		[Category("Action")]
		[Description("Event, který bude vyvolán opakovaně v průběhu interaktivní (výhradně !) změny výšky záhlaví")]
		public event EventHandler ColumnHeaderHeightChanging;
		/// <summary>
		/// Event, který bude vyvolán po dokončení interaktivní (výhradně !) změny výšky záhlaví
		/// </summary>
		[Browsable(true)]
		[Category("Action")]
        [Description("Event, který bude vyvolán po dokončení interaktivní (výhradně !) změny a při každé neinteraktivní změně výšky záhlaví")]
		public event EventHandler ColumnHeaderHeightChanged;
		/// <summary>
        /// Event, který bude vyvolán opakovaně v průběhu interaktivní (výhradně !) změny šířky konkrétního sloupce
        /// </summary>
        [Browsable(true)]
        [Category("Action")]
        [Description("Event, který bude vyvolán opakovaně v průběhu interaktivní (výhradně !) změny šířky některého sloupce")]
        public event DColumnWidthEventHandler ColumnWidthChanging;
        /// <summary>
        /// Event, který bude vyvolán po dokončení interaktivní (výhradně !) změny šířky konkrétního sloupce
        /// </summary>
        [Browsable(true)]
        [Category("Action")]
        [Description("Event, který bude vyvolán po změně šířky některého sloupce")]
        public event DColumnWidthEventHandler ColumnWidthChanged;
        /// <summary>
        /// Event, který bude vyvolán po změně pořadí sloupce (a přepočtu pořadí ostatních sloupců)
        /// </summary>
        [Browsable(true)]
        [Category("Action")]
        [Description("Event, který bude vyvolán po změně pořadí sloupce (a přepočtu pořadí ostatních sloupců)")]
        public event DColumnEventHandler ColumnOrderChanged;
        /// <summary>
        /// Event, který bude vyvolán po změně režimu zobrazování hlavičky (HeaderMode) konkrétního sloupce
        /// </summary>
        [Browsable(true)]
        [Category("Action")]
        [Description("Event, který bude vyvolán po změně režimu zobrazování hlavičky (HeaderMode) konkrétního sloupce")]
        public event DColumnEventHandler ColumnHeaderModeChanged;
        /// <summary>
        /// Event, který bude vyvolán po změně hodnoty Visible konkrétního sloupce
        /// </summary>
        [Browsable(true)]
        [Category("Action")]
        [Description("Event, který bude vyvolán po změně hodnoty Visible konkrétního sloupce")]
        public event DColumnEventHandler ColumnVisibleChanged;
        /// <summary>
        /// Event, který bude vyvolán po jakékoli změně počtu sloupců (Add, Remove, Clear)
        /// </summary>
        [Browsable(true)]
        [Category("Action")]
        [Description("Event, který bude vyvolán po jakékoli změně počtu sloupců (Add, Remove, Clear)")]
        public event EventHandler ColumnCountChanged;
        /// <summary>
        /// Event, který bude vyvolán po jakékoli změně v řádkovém filtru
        /// </summary>
        [Browsable(true)]
        [Category("Action")]
        [Description("Event, který bude vyvolán po jakékoli změně v řádkovém filtru")]
        public event DColumnRowFilterEventHandler RowFilterChanged;
        /// <summary>
        /// Event, který bude vyvolán po změně třídění na některém sloupci
        /// </summary>
        [Browsable(true)]
        [Category("Action")]
        [Description("Event, který bude vyvolán po změně třídění na některém sloupci")]
        public event DColumnEventHandler ColumnSortChanged;
        #endregion
        #region SLUŽBY PRO COLUMN
        internal int GetLeft(DColumn column)
		{
			int colIdx = this.Columns.FindIndex(c => Object.ReferenceEquals(c, column));
			if (colIdx < 0)
				return -1;
			int left = 0;
			for (int i = 0; i < colIdx; i++)
				left += this.Columns[i].VisibleWidth;

			return left;
		}
		#endregion
		#region COLUMNS ADD, GET, REMOVE
        /// <summary>
        /// Počet sloupců
        /// </summary>
		public int ColumnsCount { get { return this.Columns.Count; } }
        /// <summary>
        /// Vymaže všechny columny.
        /// </summary>
        public void ColumnsClear()
        {
            this._ColumnsClear();
        }
        /// <summary>
        /// Přidá sloupec.
        /// Vrátí jej (lze tak nastavit jeho další vlastnosti).
        /// </summary>
        /// <param name="text">Text v záhlaví sloupci</param>
        /// <returns></returns>
		public DColumn ColumnAdd(string text)
		{
			DColumn column = new DColumn(this, null, text, 150);
			this._ColumnAdd(column);
			return column;
		}
        /// <summary>
        /// Přidá sloupec.
        /// Vrátí jej (lze tak nastavit jeho další vlastnosti).
        /// </summary>
        /// <param name="text">Text v záhlaví sloupci</param>
        /// <param name="key">Název sloupce, pod nímž jej lze vyhledat</param>
        /// <returns></returns>
		public DColumn ColumnAdd(string text, string key)
		{
			DColumn column = new DColumn(this, key, text, 150);
			this._ColumnAdd(column);
			return column;
		}
        /// <summary>
        /// Přidá sloupec.
        /// Vrátí jej (lze tak nastavit jeho další vlastnosti).
        /// </summary>
        /// <param name="text">Text v záhlaví sloupci</param>
        /// <param name="width">Šířka sloupce</param>
        /// <returns></returns>
        public DColumn ColumnAdd(string text, int width)
		{
			DColumn column = new DColumn(this, null, text, width);
			this._ColumnAdd(column);
			return column;
		}
        /// <summary>
        /// Přidá sloupec.
        /// Vrátí jej (lze tak nastavit jeho další vlastnosti).
        /// </summary>
        /// <param name="text">Text v záhlaví sloupci</param>
        /// <param name="key">Název sloupce, pod nímž jej lze vyhledat</param>
        /// <param name="width">Šířka sloupce</param>
        /// <returns></returns>
        public DColumn ColumnAdd(string text, string key, int width)
		{
			DColumn column = new DColumn(this, key, text, width);
			this._ColumnAdd(column);
			return column;
		}
        /// <summary>
        /// Najde a vrátí sloupec podle jeho názvu.
        /// Pokud nenajde, vrátí null.
        /// </summary>
        /// <param name="key">Název sloupce, pod nímž jej lze vyhledat</param>
        /// <returns></returns>
		public DColumn ColumnGet(string key)
		{
			return ColumnGet(this._ColumnFindIndex(key));
		}
        /// <summary>
        /// Najde a vrátí sloupec podle jeho logického indexu.
        /// Pokud nenajde, vrátí null.
        /// </summary>
        /// <param name="index">Index sloupce</param>
        /// <returns></returns>
        public DColumn ColumnGet(int index)
		{
            // Vstupní hodnota "index" je logické číslo, odpovídá DColumn.Index, ale takový sloupec leží v poli this.Columns na fyzické pozici o 1 vyšší, než je logický index. 
            // Na fyzickém indexu [0] leží systémový column.
            // Datové columny tedy mají prvek s indexem [0] uložený v prvku [1], and so on:
            int idx = _GetPhysicalIdx(index, false);
            if (idx < 0) return null;
			return this.Columns[idx];
		}
        /// <summary>
        /// Obsahuje system column
        /// </summary>
        protected DColumn SystemColumn
        {
            get
            {
                if (this.Columns.Count > 0 && this.Columns[0].IsSystemColumn)
                    return this.Columns[0];
                return null;
            }
        }
        /// <summary>
        /// Najde index sloupce podle jeho názvu.
        /// </summary>
        /// <param name="key">Název sloupce, pod nímž jej lze vyhledat</param>
        /// <returns></returns>
        public int ColumnFindIndex(string key)
        {
            return this._ColumnFindIndex(key);
        }
        /// <summary>
        /// Najde index daného sloupce.
        /// </summary>
        /// <param name="key">Název sloupce, pod nímž jej lze vyhledat</param>
        /// <returns></returns>
        public int ColumnFindIndex(DColumn column)
        {
            return this._ColumnFindIndex(column);
        }
        /// <summary>
        /// Odebere sloupec podle jeho názvu.
        /// Pokud jej nenajde, nic nedělá.
        /// </summary>
        /// <param name="key">Název sloupce, pod nímž jej lze vyhledat</param>
        /// <returns></returns>
        public void ColumnRemove(string key)
        {
            _ColumnRemove(ColumnGet(key));
        }
        /// <summary>
        /// Odebere sloupec podle jeho indexu.
        /// Pokud jej nenajde, nic nedělá.
        /// </summary>
        /// <param name="index">Index sloupce</param>
        /// <returns></returns>
        public void ColumnRemove(int index)
        {
            _ColumnRemove(ColumnGet(index));
        }
        /// <summary>
        /// Odebere daný sloupec.
        /// Pokud jej nenajde, nic nedělá.
        /// </summary>
        /// <param name="column">Sloupec</param>
        /// <returns></returns>
        public void ColumnRemove(DColumn column)
        {
            _ColumnRemove(column);
        }
		/// <summary>
		/// Vrátí soupis columns, které jsou viditelné, v pořadí ve kterém mají být zobrazeny
		/// </summary>
		/// <returns></returns>
		public List<DColumn> GetVisibleColumns()
		{
            return this._GetVisibleColumns(false);
		}
        /// <summary>
        /// Vrátí soupis columns, které jsou viditelné, v pořadí ve kterém mají být zobrazeny.
        /// Lze zvolit, zda jako první sloupec bude vrácen systémový sloupec (headers).
        /// </summary>
        /// <param name="withSysHeader">true = včetně systémového headeru (ten má příznak IsSystem = true, a Index = -1)</param>
        /// <returns></returns>
        internal List<DColumn> GetVisibleColumns(bool withSysHeader)
        {
            return this._GetVisibleColumns(withSysHeader);
        }
        /// <summary>
        /// Vrátí soupis columns, které jsou viditelné, v pořadí ve kterém mají být zobrazeny.
        /// Lze zvolit, zda jako první sloupec bude vrácen systémový sloupec (headers).
        /// </summary>
        /// <param name="withSysHeader">true = včetně systémového headeru (ten má příznak IsSystem = true, a Index = -1)</param>
        /// <param name="recalcBounds">true = předem provést přepočet DColumn.Bound a DColumn.BoundInner (prostory, kam bude sloupec vykreslován)</param>
        /// <returns></returns>
        internal List<DColumn> GetVisibleColumns(bool withSysHeader, bool recalcBounds)
        {
            if (recalcBounds)
                this._RecalculateColumnBounds();

            return this._GetVisibleColumns(withSysHeader);
        }
        /// <summary>
        /// Vrátí soupis columns, které jsou viditelné, v pořadí ve kterém mají být zobrazeny.
        /// Lze zvolit, zda jako první sloupec bude vrácen systémový sloupec (headers).
        /// </summary>
        /// <param name="withSysHeader">true = včetně systémového headeru (ten má příznak IsSystem = true, a Index = -1)</param>
        /// <param name="recalcBounds">true = předem provést přepočet DColumn.Bound a DColumn.BoundInner (prostory, kam bude sloupec vykreslován)</param>
        /// <returns></returns>
        private List<DColumn> _GetVisibleColumns(bool withSysHeader)
        {
            List<DColumn> result = this.Columns.FindAll(c => (c.VisibleWidth > 0 && (withSysHeader || !c.IsSystemColumn)));
            result.Sort(DColumn.CompareByOrder);
            return result;
        }
        /// <summary>
        /// Vrátí soupis všech columns, nehledě na viditelné / neviditelné a na systémové, prostě všechny.
        /// Mohou být setříděné, podle parametru sorted.
        /// </summary>
        /// <param name="sorted">Požadavek na setřídění seznamu podle pořadí Order (false = budou v nativním pořadí dle indexu).</param>
        /// <returns></returns>
        internal List<DColumn> GetAllColumns(bool sorted)
        {
            List<DColumn> result = new List<DColumn>(this.Columns);
            if (sorted)
                result.Sort(DColumn.CompareByOrder);
            return result;
        }
        /// <summary>
        /// Přepočte ColumnOrder všech sloupců tak, aby bylo korektní.
        /// </summary>
        internal void ColumnOrderRecalc()
        {
            List<DColumn> columns = this.GetAllColumns(true);
            int columnOrder = 0;
            for (int c = 0; c < columns.Count; c++)
            {
                DColumn column = columns[c];
                if (column.IsSystemColumn)
                    column.ColumnOrderInternal = -1;
                else
                    column.ColumnOrderInternal = columnOrder++;
            }
        }
        #region privátní výkonné metody
        private void _ColumnsClear()
        {
            this.Columns.Clear();
            this.ColumnIdx.Clear();
            this._ColumnAddSystem();
            this.OnColumnCountChanged();
        }
        /// <summary>
        /// Přidá do kolekce systémový column. Pozor, pokud kolekce není prázdná, nepřidá jej ale vyhodí chybu.
        /// </summary>
        private void _ColumnAddSystem()
        {
            if (this.Columns.Count > 0)
                throw new InvalidOperationException("Cannot insert system column into DColumns, collection Columns is not empty.");

            this._ColumnAdd(DColumn.CreateSystemColumn(this));
        }
		private void _ColumnAdd(DColumn column)
		{
			if (!String.IsNullOrEmpty(column.Key))
			{
				if (this.ColumnIdx.ContainsKey(column.Key))
					throw new ArgumentException("Duplicite key " + column.Key + " in DColumns.", "Key");
				this.ColumnIdx.Add(column.Key, this.Columns.Count);
			}
			this.Columns.Add(column);
            this.OnColumnCountChanged();
        }
        private void _ColumnRemove(DColumn column)
        {
            if (column == null) return;
            if (column.IsSystemColumn)
                throw new InvalidOperationException("Cannot remove system column from collection DColumns.Columns.");

            if (!String.IsNullOrEmpty(column.Key))
            {
                if (this.ColumnIdx.ContainsKey(column.Key))
                    this.ColumnIdx.Remove(column.Key);
            }
            int index = _ColumnFindIndex(column);
            if (index >= 0)
            {
                this.Columns.RemoveAt(index);
                this.OnColumnCountChanged();
            }
        }
        /// <summary>
        /// Metoda vrátí fyzický index prvku, jehož logický index je daný.
        /// Mezi tím je offset 1: logický prvek [0] je uložen na fzickém indexu [1].
        /// To proto, že první je vždy uložen systémový column, jehož logický index je [-1], a je uložen na pozici [0].
        /// Metoda vrátí logický index, anebo -1 když je požadován neexistující prvek.
        /// Požadavek na systémový index -1 musí být podpořen parametrem withSysColumn = true.
        /// </summary>
        /// <param name="index"></param>
        /// <param name="withSysColumn"></param>
        /// <returns></returns>
        private int _GetPhysicalIdx(int index, bool withSysColumn)
        {
            if (index < 0 && !withSysColumn) return -1;              // Požadavek na záporný index (tj. i systémový) bez podpory parametru: -1
            int idx = index + 1;                                     // Fyzický index = logický + 1
            if (idx < 0 || idx > this.Columns.Count) return -1;      // Reálně neexistuje
            return idx;
        }
        private int _ColumnFindIndex(string key)
		{
			if (String.IsNullOrEmpty(key)) return -1;
			int index;
			if (!this.ColumnIdx.TryGetValue(key, out index))
				return -1;
			return index;
		}
        private int _ColumnFindIndex(DColumn column)
        {
            if (column == null) return -1;
            int index = this.Columns.FindIndex(c => Object.ReferenceEquals(c, column));
            return index;
        }
        #endregion
        #endregion
        #region COLUMN LINES
        /// <summary>
        /// Vrátí true, pokud daný bod leží na čáře oddělující sloupce (pravá strana některého sloupce).
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        public bool IsPointAtColumnLine(Point point, int border)
        {
            DColumn column = _FindColumnByRightLine(point.X, border);
            return (column != null);
        }
        /// <summary>
        /// Najde sloupec, jehož pravá hrana se nachází na dané souřadnici.
        /// Umožní hledat s tolerancí.
        /// </summary>
        /// <param name="x">Souřadnice X, na které hledáme hranu Right</param>
        /// <param name="border">Tolerance bodu</param>
        /// <returns></returns>
        private DColumn _FindColumnByRightLine(int x, int border)
        {
            Dictionary<int, DColumn> colLines = ColumnLines;
            int b = (border < 0 ? 0 : (border > 25 ? 25 : border));
            for (int t = 0; t <= b; t++)
            {
                DColumn column;
                if (colLines.TryGetValue((x - t), out column)) return column;
                if (colLines.TryGetValue((x + 1 + t), out column)) return column;
            }
            return null;
        }
        /// <summary>
        /// Obsahuje seznam souřadnic Right každého viditelného sloupce, a referenci na tento sloupec.
        /// Viditelný sloupec má vždy šířku větší než 0, tedy souřadnice Right viditelných sloupců je vždy unikátní.
        /// </summary>
        protected Dictionary<int, DColumn> ColumnLines
        {
            get
            {
                if (this._ColumnLines == null)
                {
                    Dictionary<int, DColumn> colLines = new Dictionary<int, DColumn>();

                    int right = 0;
                    foreach (DColumn column in this.Columns)
                    {
                        int width = column.VisibleWidth;
                        if (width > 0)
                        {
                            right += width;
                            colLines.Add(right, column);
                        }
                    }

                    this._ColumnLines = colLines;
                }
                return this._ColumnLines;
            }
        }
        /// <summary>
        /// Zajistí invalidaci paměti svislých souřadnic Right sloupců.
        /// Je nutno volat po změně šířky a viditelnosti každého sloupce.
        /// </summary>
        private void _ColumnLinesInvalidate()
        {
            this._ColumnLines = null;
        }
		/// <summary>
		/// Metoda zajistí převzetí šířky všech sloupců z dodaného pole.
		/// Slouží k hromadnému načtení šířek. Nevyvolává žádné eventy ani Draw().
        /// Příští Draw() překreslí obsah.
		/// Odpovídající reciproční metoda je ColumnWidthExport().
		/// </summary>
		/// <param name="widthList"></param>
        internal void ColumnWidthImportFrom(List<int> widthList, bool interactive)
		{
			int cnt = Math.Min(this.Columns.Count, widthList.Count);
			for (int c = 0; c < cnt; c++)
				this.Columns[c].WidthInternal = widthList[c];
            if (this.ContainColumnToFillWidth)
                this.RecalculateColumnWidthToFill(interactive, true);
            this._TimeAxisSetBounds(true);
            this._ColumnLinesInvalidate();
		}
        /// <summary>
        /// Metoda zajistí převzetí výšky záhlaví z dodaného layoutu.
        /// Slouží k hromadnému načtení dat. Nevyvolává žádné eventy ani Draw().
        /// Příští Draw() překreslí obsah.
        /// </summary>
        /// <param name="graphLayout"></param>
        internal void ColumnHeaderHeightImportFrom(GridPanelLayoutGraph graphLayout, bool interactive)
        {
            if (interactive)
            {
                this._ColumnHeaderHeightInteractive = graphLayout.HeaderHeight;
            }
            else
            {
                this._ColumnHeaderHeight = graphLayout.HeaderHeight;
                this._ColumnHeaderHeightInteractive = graphLayout.HeaderHeight;
            }
            // Výška řádkového filtru:
            // this._FilterHeight = graphLayout.HeaderFilterHeight;
            this.Height = graphLayout.HeaderHeight;
        }
        /// <summary>
		/// Metoda zajistí sestavení a vrácení seznamu šířky všech sloupců.
		/// Slouží k hromadnému ukládání (persistenci) šířek.
		/// Odpovídající reciproční metoda je ColumnWidthImportFrom().
		/// </summary>
		internal List<int> ColumnWidthExport()
		{
			List<int> widthList = new List<int>();
			int cnt = this.Columns.Count;
			for (int c = 0; c < cnt; c++)
				widthList.Add(this.Columns[c].WidthInternal);
			return widthList;
		}
		private Dictionary<int, DColumn> _ColumnLines;
        #endregion
        #region MYŠ
        #region OBSLUHA NATIVNÍCH EVENTŮ A JEJICH DETEKCE, ROZDĚLOVÁNÍ DO FUNKČNÍCH BLOKŮ
        void _MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.None)
                _MouseMoveNone(e);
            else if (e.Button == System.Windows.Forms.MouseButtons.Left)
                _MouseMoveLeftButton(e);
            else if (e.Button == System.Windows.Forms.MouseButtons.Right)
                _MouseMoveRightButton(e);
        }
        /// <summary>
        /// Myš se pohnula, tlačítka nejsou stisknuta.
        /// </summary>
        /// <param name="e"></param>
        private void _MouseMoveNone(MouseEventArgs e)
        {
            MouseStatus mStatus = _MouseCurrentDetect(e, false);
			// if (MouseStatus.IsMouseChanged(mStatus, this._CurrentMouseStatus))
			//     Steward.AuditInfo("MouseMove, change status from " + this._CurrentMouseStatus.CurrentMouseMode.ToString() + " ... to " + mStatus.CurrentMouseMode.ToString());
            _MouseApplyNewStatus(mStatus);
        }
        /// <summary>
        /// Myš se hýbe a má zmáčknutý levý button = provádí se Resize nebo Move
        /// </summary>
        /// <param name="e"></param>
        private void _MouseMoveLeftButton(MouseEventArgs e)
        {
            if (this._CurrentMouseStatus != null)
            {
                switch (this._CurrentMouseStatus.CurrentMouseMode)
                {
                    case MouseMode.OnColumnLeftButton:
                        _MouseColumnHeaderMoveOnColumn(e);
                        break;
                    case MouseMode.ResizeColumnWidth:
                        _MouseColumnWidthResizeRun(e);
                        break;
                    case MouseMode.ResizeColumnHeaderHeight:
                        _MouseColumnHeaderHeightResizeRun(e);
                        break;
                    case MouseMode.MoveColumnsVertical:
                        this._MouseColumnHeaderVerticalMoveRun(e);
                        break;
                    case MouseMode.ResizeParent:
                        this._MouseColumnHeaderParentResizeRun(e);
                        break;
                }
            }
        }
        /// <summary>
        /// Zajistí detekci a zpracování pohybu myši při stisknutém levém buttonu, když je myš stisknuta na columnu.
        /// V tuto chvíli není ještě rozhodnuto o druhu pohybu, k tomu může dojít právě zde:
        /// - column lze přetáhnout doleva/doprava a změnit tak jeho ColumnOrder,
        /// - anebo nahoru/dolů, a měnit tak pozici Top, pokud je to povoleno.
        /// </summary>
        /// <param name="e"></param>
        private void _MouseColumnHeaderMoveOnColumn(MouseEventArgs e)
        {
            if (this._CurrentMouseStatus == null) return;
            Rectangle mouseDragArea = this._CurrentMouseStatus.MouseDragArea;
            if (mouseDragArea.IsEmpty) return;

            // Detekce směru pohybu:
            if (mouseDragArea.Contains(e.Location))
                // Současná pozice myši (e.Location) je uvnitř prostoru, kde ignorujeme zahájení Drag (MouseDragArea):
                return;

            // Vlevo/Vpravo:
            if ((e.Location.X < mouseDragArea.X || e.Location.X > mouseDragArea.Right) && this._CurrentMouseStatus.ColumnActive != null && this._CurrentMouseStatus.ColumnActive.IsMovable)
            {   // Myš je mimo prostor mouseDragArea ve směru X, a známe aktivní Column, a ten je možné přemisťovat?
                // Zahájíme změnu ColumnOrder sloupce this._CurrentMouseStatus.ColumnActive:

                //  Zatím není implementováno:
                // this._CurrentMouseStatus.CurrentMouseMode = MouseMode.MoveColumnHorizontal;
                // this._MouseColumnHorizontalMoveBegin();                // Přesouvání zahájíme (teď, o něco opožděně po stisku tlačítka, protože jsme čekali na pohyb)
                // this._MouseColumnHorizontalMoveRun(e);                 // Rovnou provedeme pohyb, ať je vidět že se začalo
                // return;
            }

            // Nahoru/Dolu :
            if (e.Location.Y < mouseDragArea.Y || e.Location.Y > mouseDragArea.Bottom)
            {   // Myš je mimo prostor mouseDragArea ve směru Y; je možné přemisťovat záhlaví?
                switch (this.ColumnHeaderMoveMode)
                {
                    case DColumnsHeaderMoveMode.MoveInParent:
                        this._CurrentMouseStatus.CurrentMouseMode = MouseMode.MoveColumnsVertical;
                        this._MouseColumnHeaderVerticalMoveBegin();            // Přesouvání zahájíme (teď, o něco opožděně po stisku tlačítka, protože jsme čekali na pohyb)
                        this._MouseColumnHeaderVerticalMoveRun(e);             // Rovnou provedeme pohyb, ať je vidět že se začalo
                        return;
                    case DColumnsHeaderMoveMode.ResizeParent:
                        this._CurrentMouseStatus.CurrentMouseMode = MouseMode.ResizeParent;
                        this._MouseColumnHeaderParentResizeBegin();            // Přesouvání zahájíme (teď, o něco opožděně po stisku tlačítka, protože jsme čekali na pohyb)
                        this._MouseColumnHeaderParentResizeRun(e);             // Rovnou provedeme pohyb, ať je vidět že se začalo
                        return;
                }
            }
        }
        /// <summary>
        /// Myš se hýbe se zmáčknutým pravým buttonem
        /// </summary>
        /// <param name="e"></param>
        private void _MouseMoveRightButton(MouseEventArgs e)
        {
            
        }
		/// <summary>
		/// Myš se zmáčkla
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
        void _MouseDown(object sender, MouseEventArgs e)
        {
            MouseStatus mStatus = _MouseCurrentDetect(e, true);
            _MouseApplyNewStatus(mStatus);

            // Podle situace zajistíme některé další věci - a to okamžitě (jiné režimy zahájíme až po detekci zahájení pohybu):
            switch (mStatus.CurrentMouseMode)
            {
                case MouseMode.ResizeColumnWidth:
                    _MouseColumnWidthResizeBegin();
                    break;
                case MouseMode.ResizeColumnHeaderHeight:
                    _MouseColumnHeaderHeightResizeBegin();
                    break;
            }
        }
		/// <summary>
		/// Myš se zvedla
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
        void _MouseUp(object sender, MouseEventArgs e)
        {
            MouseStatus mStatus = _MouseCurrentDetect(e, false);

            // Vyřešíme situaci podle toho, na co se kliklo:
            switch (this._CurrentMouseStatus.CurrentMouseMode)
            {
                case MouseMode.ResizeColumnWidth:
                    _MouseColumnWidthResizeEnd();
                    break;
                case MouseMode.ResizeColumnHeaderHeight:
                    _MouseColumnHeaderHeightResizeEnd();
                    break;
                case MouseMode.OnColumnLeftButton:
                    _MouseLeftClickOnColumn();
                    break;
                case MouseMode.MoveColumnsVertical:
                    this._MouseColumnHeaderVerticalMoveEnd();
                    break;
                case MouseMode.ResizeParent:
                    this._MouseColumnHeaderParentResizeEnd();
                    break;
            }

            _MouseApplyNewStatus(mStatus);
        }
        /// <summary>
        /// Myš opouští control
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _MouseLeave(object sender, EventArgs e)
        {
			// Steward.AuditInfo("MouseLeave.");
            MouseStatus mStatus = this._CurrentMouseStatus;
            if (mStatus != null)
                mStatus.ClearOnLeave();             // Vyprázdním status, ponechám tam jen referenci na poslední hot column
            _MouseApplyNewStatus(mStatus);
        }
		/// <summary>
		/// Nastaví explicitní stav myši. 
		/// Používá se při pohybu myši nad konstrukčně cizím elementem, který ale logicky má být podmnožinou některého columnu.
		/// Typicky textbox v řádkovém filtru.
		/// </summary>
		/// <param name="mouseMode"></param>
		/// <param name="columnAtMouse"></param>
		/// <param name="columnActive"></param>
        internal void SetMouseStatus(MouseMode mouseMode, DColumn columnAtMouse, DColumn columnActive)
        {
			// Steward.AuditInfo("MouseSetStatus: " + mouseMode.ToString());
            MouseStatus mStatus = new MouseStatus(mouseMode, columnAtMouse, null);
            _MouseApplyNewStatus(mStatus);
            this.Draw();
        }
        #endregion
        #region DETECT POSITION, MOUSE STATUS, DETECT CHANGE
        /// <summary>
        /// Aktuální stav myši, který platí v době počátku kteréhokoli myšího eventu.
        /// V průběhu zpracování myšího eventu se aktualizuje, a v čase volání výkonných metod již odráží aktuální stav.
        /// </summary>
        private MouseStatus _CurrentMouseStatus;
        /// <summary>
        /// Detekuje stav controlu dle pozice a stavu myši
        /// </summary>
        /// <param name="e"></param>
        /// <param name="mDown"></param>
        /// <returns></returns>
        private MouseStatus _MouseCurrentDetect(MouseEventArgs e, bool mDown)
        {
            if (_CurrentMouseStatus == null)
                _CurrentMouseStatus = new MouseStatus();

            MouseStatus result = new MouseStatus(e);
            DColumn column = _FindColumnByRightLine(e.Location.X, 2);
            if (column != null && column.IsResizable)
            {   // Našel jsem sloupec, který odpovídá svislé lince?
                result.ColumnActive = column;
                result.CurrentMouseMode = (mDown && e.Button == System.Windows.Forms.MouseButtons.Left ? MouseMode.ResizeColumnWidth : MouseMode.OnVerticalLine);
            }
            else if (e.Location.Y >= this.Height - 4 && this.ColumnHeaderHeightSizable)
            {   // Nacházím se na dolním okraji?
                result.CurrentMouseMode = (mDown && e.Button == System.Windows.Forms.MouseButtons.Left ? MouseMode.ResizeColumnHeaderHeight : MouseMode.OnHorizontalLine);
            }
            else
            {   // Nacházím se možná nad sloupcem:
                column = _FindColumnByPoint(e.Location);
                if (column != null)
                {
                    if (!mDown)
                    {   // Bez stisknuté myšky:
                        result.ColumnAtMouse = column;
                        result.CurrentMouseMode = MouseMode.OnColumn;
                    }
                    else
                    {   // Se stisknutou myškou:
                        result.ColumnActive = column;
                        result.CurrentMouseMode =
                            (e.Button == System.Windows.Forms.MouseButtons.Left ? MouseMode.OnColumnLeftButton :
                            (e.Button == System.Windows.Forms.MouseButtons.Right ? MouseMode.OnColumnRightButton : MouseMode.OnColumn));
                    }
                }
            }

            // Pokud je stisknutá myška, nastavím si Point, Area a Time:
            if (mDown)
            {
                result.MouseScreenPosition = Control.MousePosition;
                result.MouseDownPoint = e.Location;
                result.MouseDoubleClickArea = _GetCenteredArea(e.Location, SystemInformation.DoubleClickSize);
                result.MouseDoubleClickTime = DateTime.Now.AddMilliseconds(SystemInformation.DoubleClickTime);
                result.MouseDragArea = _GetCenteredArea(e.Location, SystemInformation.DragSize);
            }
            return result;
        }
        /// <summary>
        /// Aplikuje nový mouse status do this objektu.
        /// Změní cursor, detekuje změnu objektu, vyvolá event OnMouseChange.
        /// </summary>
        /// <param name="newStatus">Nový status</param>
        private void _MouseApplyNewStatus(MouseStatus newStatus)
        {
            MouseStatus oldStatus = this._CurrentMouseStatus;

            CursorType newCursor = (newStatus == null ? CursorType.Default : newStatus.CurrentCursorType);
            CursorType oldCursor = this._CurrentCursorType;

            if (newCursor != oldCursor)
            {
                this._CurrentCursorType = newCursor;
                this.Cursor = MouseStatus.GetCursor(newCursor);
            }

            bool mChanged = MouseStatus.IsMouseChanged(newStatus, oldStatus);

            this._CurrentMouseStatus = newStatus;

            if (mChanged)
                this.OnMouseChanged();
        }
        /// <summary>
        /// Detekuje změnu stavu controlu vlivem myši a volá event OnMouseChanged()
        /// </summary>
        /// <param name="newStatus">Nový status</param>
        private void _DetectOnChange(MouseStatus newStatus)
        {
            MouseStatus oldStatus = this._CurrentMouseStatus;
            if (oldStatus == null) return;
            if (MouseStatus.IsMouseChanged(newStatus, oldStatus))
                this.OnMouseChanged();
        }
        /// <summary>
        /// Vrátí rectangle, jehož střed leží na daném bodě, a má danou velikost.
        /// </summary>
        /// <param name="center"></param>
        /// <param name="size"></param>
        /// <returns></returns>
        private Rectangle _GetCenteredArea(Point center, Size size)
        {
            int w = size.Width;
            int h = size.Height;
            return new Rectangle(center.X - w / 2, center.Y - h / 2, w, h);
        }
        /// <summary>
        /// Najde column, který se nachází na daném pixelu
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        private DColumn _FindColumnByPoint(Point point)
        {
            return this.Columns.Find(c => c.IsOnPoint(point));
        }
        /// <summary>
        /// Třída, která obsahuje data o stavu myši, kde se nachází a co dělá.
        /// </summary>
        internal class MouseStatus
        {
            internal MouseStatus()
            {
                this.CurrentMouseMode = MouseMode.None;
            }
            internal MouseStatus(MouseEventArgs e)
            {
                this.CurrentMouseMode = MouseMode.None;
                this.MouseCurrentPoint = e.Location;
                this.MouseCurrentButtons = e.Button;
            }
            /// <summary>
            /// Konstruktor pro specifický stav
            /// </summary>
            /// <param name="mouseMode"></param>
            /// <param name="columnAtMouse"></param>
            /// <param name="columnActive"></param>
            internal MouseStatus(MouseMode mouseMode, DColumn columnAtMouse, DColumn columnActive)
            {
                this.CurrentMouseMode = mouseMode;

                if (mouseMode == MouseMode.LeaveOnFilter)
                    this.LastColumnAtMouse = columnAtMouse;
                else
                    this.ColumnAtMouse = columnAtMouse;
                
                this.ColumnActive = columnActive;
            }
            /// <summary>
            /// Aktuální stav myši
            /// </summary>
            internal MouseMode CurrentMouseMode { get; set; }
            /// <summary>
            /// Typ kurzoru (nikoli kurzor osobně), odpovídá stavu myši this.CurrentMouseMode. Vhodné pro porovnání změny.
            /// </summary>
            internal CursorType CurrentCursorType { get { return GetCursorType(this.CurrentMouseMode); } }
            /// <summary>
            /// Aktuální kurzor, odpovídá typu this.CurrentCursorType.
            /// </summary>
            internal Cursor CurrentCursor { get { return GetCursor(this.CurrentCursorType); } }
            /// <summary>
            /// Sloupec, nad nímž se nachází myš (což ještě neznamená nic jiného, než že se vykresluje jako Hot)
            /// </summary>
            internal DColumn ColumnAtMouse { get; set; }
            /// <summary>
            /// Sloupec, nad nímž se nachází myš (což ještě neznamená nic jiného, než že se vykresluje jako Hot)
            /// </summary>
            internal DColumn LastColumnAtMouse { get; set; }
            /// <summary>
            /// Sloupec, pro který se provádí nebo se bude provádět akce daná stavem myši (Click, RightClick, Reorder, Resize)
            /// </summary>
            internal DColumn ColumnActive { get; set; }
            /// <summary>
            /// Bod, na kterém se nachází myš v okamžiku její detekce.
            /// </summary>
            internal Point MouseCurrentPoint { get; set; }
            /// <summary>
            /// Buttony myši v okamžiku její detekce.
            /// </summary>
            internal MouseButtons MouseCurrentButtons { get; set; }
            /// <summary>
            /// Posun (rozdíl pozice) mezi myší a patřičnou hranou objektu.
            /// Pozice myši se určuje s jistou tolerancí (aby uživatel nemusel najet explicitně na přesný pixel).
            /// Pak v průběhu posunu objektu je třeba tuto toleranci použít, aby objekt neposkočil přesně pod myš.
            /// Hodnota se nastavuje ve fázi Begin (při MouseDown) a vyhodnocuje při pohybu (MouseMove).
            /// </summary>
            internal int MouseObjectShift { get; set; }
            /// <summary>
            /// Pozice myši v koordinátech Screenu v okamžiku jejího stisknutí (=Control.MousePosition)
            /// </summary>
            internal Point MouseScreenPosition { get; set; }
            /// <summary>
            /// Bod, na kterém se stiskla myš.
            /// Pokud se myš bude pohybovat při současném stisknutí, pak se pohyb vyhodnotí jako reálný pohyb až tehdy, 
            /// až bod myši opustí prostor MouseDragArea (ohrádka okolo bodu spuštění).
            /// </summary>
            internal Point MouseDownPoint { get; set; }
            /// <summary>
            /// Prostor, v němž myš může provést DoubleClick v čase do MouseDoubleClickTime
            /// </summary>
            internal Rectangle MouseDoubleClickArea { get; set; }
            /// <summary>
            /// Čas, do něhož myš může provést DoubleClick v prostoru MouseDoubleClickArea
            /// </summary>
            internal DateTime MouseDoubleClickTime { get; set; }
            /// <summary>
            /// Prostor, v němž se myš tváří, jako že se nepohnula (malý rámeček okolo bodu MouseDown)
            /// </summary>
            internal Rectangle MouseDragArea { get; set; }
            /// <summary>
            /// Vrací true, pokud se změnil stav kurzoru nebo vybraný sloupec
            /// </summary>
            /// <param name="newStatus"></param>
            /// <param name="oldStatus"></param>
            /// <returns></returns>
            internal static bool IsMouseChanged(MouseStatus newStatus, MouseStatus oldStatus)
            {
                bool newExists = (newStatus != null);
                bool oldExists = (oldStatus != null);
                if (!newExists && !oldExists) return false;        // Neexistuje ani jeden: žádná změna.
                if (newExists != oldExists) return true;           // Jeden existuje, druhý ne: jde o změnu.

                // Oba existují: vyhodnotíme jejich vnitřek:
                if (oldStatus.CurrentMouseMode != newStatus.CurrentMouseMode ||
                    !Object.ReferenceEquals(oldStatus.ColumnAtMouse, newStatus.ColumnAtMouse) ||
                    !Object.ReferenceEquals(oldStatus.ColumnActive, newStatus.ColumnActive))
                    return true;
                return false;
            }
            /// <summary>
            /// Vrátí druh kurzoru pro daný režim myši
            /// </summary>
            /// <param name="mouseMode"></param>
            /// <returns></returns>
            internal static CursorType GetCursorType(MouseMode mouseMode)
            {
                switch (mouseMode)
                {
                    case MouseMode.None: return CursorType.Default;
                    case MouseMode.LeaveOnFilter: return CursorType.Default;
                    case MouseMode.OnColumn: return CursorType.Default;
                    case MouseMode.OnColumnLeftButton: return CursorType.Default;
                    case MouseMode.OnColumnRightButton: return CursorType.Default;
                    case MouseMode.OnVerticalLine: return CursorType.VSplit;
                    case MouseMode.OnHorizontalLine: return CursorType.HSplit;
                    case MouseMode.ResizeColumnWidth: return CursorType.VResize;
                    case MouseMode.ResizeColumnHeaderHeight: return CursorType.HResize;
                }
                return CursorType.Default;
            }
            /// <summary>
            /// Vrátí kurzor daného typu.
            /// </summary>
            /// <param name="cursorType"></param>
            /// <returns></returns>
            internal static Cursor GetCursor(CursorType cursorType)
            {
                switch (cursorType)
                {
                    case CursorType.Default: return Cursors.Default;
                    case CursorType.VSplit: return Cursors.VSplit;
                    case CursorType.VResize: return Cursors.SizeWE;
                    case CursorType.HSplit: return Cursors.HSplit;
                    case CursorType.HResize: return Cursors.SizeNS;
                    case CursorType.HMove: return Cursors.Hand;
                }
                return Cursors.Default;
            }
            /// <summary>
            /// Vynuluje se při opouštění objektu DColumns.
            /// Ponechá si pouze referenci na posledně Hot column, a to v property LastColumnAtMouse.
            /// </summary>
            internal void ClearOnLeave()
            {
                if (this.CurrentMouseMode != MouseMode.LeaveOnFilter)
                {   // Do tohoto stavu lze objekt vynulovat jen při prvním požadavku, jinak bych při druhém poždavku přišel o referenci na objekt LastColumnAtMouse:
                    this.LastColumnAtMouse = this.ColumnAtMouse;
                    this.CurrentMouseMode = MouseMode.LeaveOnFilter;
                    this.ColumnAtMouse = null;
                    this.ColumnActive = null;
                    this.MouseCurrentPoint = Point.Empty;
                    this.MouseCurrentButtons = System.Windows.Forms.MouseButtons.None;
                    this.MouseObjectShift = 0;
                    this.MouseScreenPosition = Point.Empty;
                    this.MouseDownPoint = Point.Empty;
                    this.MouseDoubleClickArea = Rectangle.Empty;
                    this.MouseDoubleClickTime = DateTime.MinValue;
                    this.MouseDragArea = Rectangle.Empty;
                }
            }
        }
        /// <summary>
        /// Stav myši
        /// </summary>
        internal enum MouseMode
        {
            /// <summary>
            /// Myš se nenachází nad žádným prvkem (je například vpravo od posledního sloupce)
            /// </summary>
            None = 1,
            /// <summary>
            /// Myš se pravděpodobně nachází nad řádkovým filtrem sloupce LastColumnAtMouse
            /// </summary>
            LeaveOnFilter,
            /// <summary>
            /// Myš se nachází nad sloupcem ColumnAtMouse, ale uživatel nestiskl žádné tlačítko
            /// </summary>
            OnColumn,
            /// <summary>
            /// Myš se nachází nad sloupcem ColumnActive, uživatel stiskl levé tlačítko a stále jej drží (může nastat Klik, nebo Move).
            /// </summary>
            OnColumnLeftButton,
            /// <summary>
            /// Myš se nachází nad sloupcem ColumnActive, uživatel stiskl levé tlačítko a stále jej drží
            /// </summary>
            OnColumnRightButton,
            /// <summary>
            /// Myš se nachází nad svislým dělítkem sloupců, mohla by resizovat šířku sloupce vlevo od myši (ColumnActive) když by se zmáčkla (ale zatím není zmáčknutá)
            /// </summary>
            OnVerticalLine,
            /// <summary>
            /// Myš se nachází nad vodorovoným dělítkem pod sloupci, mohla by resizovat výšku ColumnHeaderHeight když by se zmáčkla (ale zatím není zmáčknutá)
            /// </summary>
            OnHorizontalLine,
            /// <summary>
            /// Myš se nacházela nad svislým dělítkem sloupců, uživatel ji zmáčkl a nyní resizuje šířku sloupce ColumnActive
            /// </summary>
            ResizeColumnWidth,
            /// <summary>
            /// Myš se nacházela nad vodorovoným dělítkem pod sloupci, uživatel ji zmáčkl a nyní resizuje výšku Columns
            /// </summary>
            ResizeColumnHeaderHeight,
            /// <summary>
            /// Posouvá vybraný sloupec ve vodorovném směru
            /// </summary>
            MoveColumnHorizontal,
            /// <summary>
            /// Posouvá celé záhlaví ve svislém směru
            /// </summary>
            MoveColumnsVertical,
            /// <summary>
            /// Přemisťuje a resizuje parenta
            /// </summary>
            ResizeParent
        }
        internal enum CursorType
        {
            Default,
            VSplit,
            VResize,
            HSplit,
            HResize,
            HMove
        }
        #endregion
        #region COLUMNS HEADER VERTICAL MOVE (Begin, Run, End)
        /// <summary>
        /// Při zahájení Vertical Move
        /// </summary>
        private void _MouseColumnHeaderVerticalMoveBegin()
        {
            this._CurrentMouseStatus.MouseObjectShift = this._CurrentMouseStatus.MouseScreenPosition.Y - this.Top;
            // Tady by se hodilo posunout objekt vizuálně navrch (ZOrder). Ale jak?
        }
        /// <summary>
        /// Provádí Vertical Move
        /// </summary>
        /// <param name="e"></param>
        private void _MouseColumnHeaderVerticalMoveRun(MouseEventArgs e)
        {
            this.ColumnHeaderTopInteractive = Control.MousePosition.Y - this._CurrentMouseStatus.MouseObjectShift;
        }
        /// <summary>
        /// Po dokončení Vertical Move
        /// </summary>
        private void _MouseColumnHeaderVerticalMoveEnd()
        {
            this.ColumnHeaderTop = this.ColumnHeaderTopInteractive;
        }
        #endregion
        #region COLUMNS HEADER PARENT MOVE (Begin, Run, End)
        /// <summary>
        /// Při zahájení Parent Resize
        /// </summary>
        private void _MouseColumnHeaderParentResizeBegin()
        {
            this._CurrentMouseStatus.MouseObjectShift = this._CurrentMouseStatus.MouseScreenPosition.Y - this.Parent.Top;
        }
        /// <summary>
        /// Provádí Parent Resize
        /// </summary>
        /// <param name="e"></param>
        private void _MouseColumnHeaderParentResizeRun(MouseEventArgs e)
        {
            Rectangle boundsOld = this.Parent.Bounds;
            int bottom = boundsOld.Bottom;                // Má zůstat konstantní
            int topNew = Control.MousePosition.Y - this._CurrentMouseStatus.MouseObjectShift;   // Ke by měl být Top našeho parenta
            topNew = _TopParentIntoRange(topNew);         // Top Parenta zarovnáme dle limitů
            Rectangle boundsNew = new Rectangle(boundsOld.X, topNew, boundsOld.Width, bottom - topNew);
			if (!Rectangle.Equals(boundsNew, boundsOld))
			{
				this.Parent.Bounds = boundsNew;           // Tento event si musí odchytit parent.
				this.OnColumnHeaderParentMoving();        // Anebo si může chytat tento event.  Každopádně nemusí měnit this.Bounds, to už je hotové.
			}
        }
        /// <summary>
        /// Po dokončení Parent Resize
        /// </summary>
        private void _MouseColumnHeaderParentResizeEnd()
        {
			this.OnColumnHeaderParentMoving();
		}
        #endregion
        #region COLUMN HEADER HEIGHT RESIZE (Begin, Run, End)
        /// <summary>
        /// Při zahájení Resize ColumnHeader
        /// </summary>
        private void _MouseColumnHeaderHeightResizeBegin()
        {
            this._CurrentMouseStatus.MouseObjectShift = this._CurrentMouseStatus.MouseDownPoint.Y - this.ColumnHeaderHeightInteractive;
        }
        /// <summary>
        /// Resizuje výšku ColumnHeader
        /// </summary>
        /// <param name="e"></param>
        private void _MouseColumnHeaderHeightResizeRun(MouseEventArgs e)
        {
			// Změna hodnoty ColumnHeaderHeightInteractive způsobí vyvolání eventu Changing, ale ne Changed:
			this.ColumnHeaderHeightInteractive = e.Location.Y - this._CurrentMouseStatus.MouseObjectShift;
            this.Draw();
        }
        /// <summary>
        /// Po dokončení resize ColumnHeader
        /// </summary>
        private void _MouseColumnHeaderHeightResizeEnd()
        {
			this.ColumnHeaderHeight = this.ColumnHeaderHeightInteractive;
        }
        #endregion
        #region COLUMN WIDTH RESIZE (Begin, Run, End)
        /// <summary>
        /// Při zahájení Resize sloupce
        /// </summary>
        private void _MouseColumnWidthResizeBegin()
        {
            DColumn column = this._CurrentMouseStatus.ColumnActive;
            if (column == null) return;
            this._CurrentMouseStatus.MouseObjectShift = this._CurrentMouseStatus.MouseDownPoint.X - column.Right;
        }
        /// <summary>
        /// Resizuje šířku aktuálního sloupce
        /// </summary>
        /// <param name="e"></param>
        private void _MouseColumnWidthResizeRun(MouseEventArgs e)
        {
            DColumn column = this._CurrentMouseStatus.ColumnActive;
            if (column == null) return;
            int width = e.Location.X - this._CurrentMouseStatus.MouseObjectShift - column.Left;
            column.WidthInteractive = width;        // Sloupec sám detekuje změnu hodnoty, a po změně vyvolá event this.OnColumnWidthChanging() (protože se mění WidthInteractive)
        }
        /// <summary>
        /// Po dokončení resize aktuálního sloupce
        /// </summary>
        private void _MouseColumnWidthResizeEnd()
        {
            DColumn column = this._CurrentMouseStatus.ColumnActive;
            if (column == null) return;
            // Nyní opíšu interaktivní hodnotu Width do trvalé:
            column.Width = column.WidthInteractive; // Sloupec sám detekuje změnu hodnoty, a po změně vyvolá event this.OnColumnWidthChanged() (protože se mění Width)
        }
        #endregion
        #region CLICK
        /// <summary>
        /// Došlo ke kliknutí a uvolnění levé myši na Columnu, bez pohybu
        /// </summary>
        private void _MouseLeftClickOnColumn()
        {
            if (this._CurrentMouseStatus.ColumnActive == null) return;

            this._SetSortByColumn(this._CurrentMouseStatus.ColumnActive);
        }
        #endregion
        #region DETEKCE PRO KONKRÉTNÍ SLOUPCE
        /// <summary>
        /// Vrátí true, pokud daný column je Hot (tj. je pod myší, myš není zmáčknutá)
        /// </summary>
        /// <param name="column">Sloupec, na který se ptáme</param>
        /// <returns></returns>
        internal bool IsColumnHot(DColumn column)
        {
            // Není sloupec => není Hot:
            if (column == null) return false;

            // Nejprve určím, zda některý Column má focus, a pokud ano, pak vrátím Hot jen pro něj:
            DColumn colFocus = this.Columns.Find(c => c.RowFilterHasFocus);
            if (colFocus != null)
                return (Object.ReferenceEquals(colFocus, column));

            // Pak určím Hot column jako ten, nad kterým je myš (to máme uloženo v this._CurrentMouseStatus.LastColumnAtMouse nebo this._CurrentMouseStatus.ColumnAtMouse):
            if (this._CurrentMouseStatus != null && column != null)
            {
                if (this._CurrentMouseStatus.CurrentMouseMode == MouseMode.LeaveOnFilter && this._CurrentMouseStatus.LastColumnAtMouse != null
                    && Object.ReferenceEquals(this._CurrentMouseStatus.LastColumnAtMouse, column) && this._CurrentMouseStatus.LastColumnAtMouse.RowFilterHasMouse)
                    return true;

                if (this._CurrentMouseStatus.CurrentMouseMode == MouseMode.OnColumn && this._CurrentMouseStatus.ColumnAtMouse != null 
                    && Object.ReferenceEquals(this._CurrentMouseStatus.ColumnAtMouse, column))
                    return true;
            }
            return false;
        }
        /// <summary>
        /// Vrátí true, pokud daný column je zmáčknutý levou myší
        /// </summary>
        /// <param name="column">Sloupec, na který se ptáme</param>
        /// <returns></returns>
        internal bool IsColumnMouseDownLeft(DColumn column)
        {
            return (this._CurrentMouseStatus != null && column != null && this._CurrentMouseStatus.ColumnActive != null &&
                this._CurrentMouseStatus.CurrentMouseMode == MouseMode.OnColumnLeftButton &&
                Object.ReferenceEquals(this._CurrentMouseStatus.ColumnActive, column));
        }
        /// <summary>
        /// Vrátí true, pokud daný column je zmáčknutý pravou myší
        /// </summary>
        /// <param name="column">Sloupec, na který se ptáme</param>
        /// <returns></returns>
        internal bool IsColumnMouseDownRight(DColumn column)
        {
            return (this._CurrentMouseStatus != null && column != null && this._CurrentMouseStatus.ColumnActive != null &&
                this._CurrentMouseStatus.CurrentMouseMode == MouseMode.OnColumnRightButton &&
                Object.ReferenceEquals(this._CurrentMouseStatus.ColumnActive, column));
        }

        #endregion
        #endregion
        #region PAINT
        /// <summary>
        /// Vykreslení celého záhlaví (interně se odtud volá kreslení sloupců)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected override void OnPaintToBuffer(object sender, PaintEventArgs e)
        {
            // Background:
            e.Graphics.Clear(this.BackColor);

            // Sloupce:
            List<DColumn> columns = this._GetVisibleColumns(true);
            foreach (DColumn column in columns)
				column.Paint(sender, e);

            // Explicitně mohu vykreslit některé linky při resize:
            _PaintLines(e);

            //e.Graphics.Flush(System.Drawing.Drawing2D.FlushIntention.Sync);
        }
        /// <summary>
        /// Zajistí přepočet DColumn.Bound a DColumn.BoundInner (prostory, kam bude sloupec vykreslován)
        /// </summary>
        private void _RecalculateColumnBounds()
        {
            List<DColumn> columns = this._GetVisibleColumns(true);
            foreach (DColumn column in columns)
                column.CalculateBounds();
        }
        /// <summary>
        /// Vyvolá vykreslení patřičných linek Resize
        /// </summary>
        /// <param name="e"></param>
        private void _PaintLines(PaintEventArgs e)
        {
            MouseMode mouseMode = (this._CurrentMouseStatus == null ? MouseMode.None : this._CurrentMouseStatus.CurrentMouseMode);   // Stav myši

            // Kreslit vodorovnou dolní linku, pokud se nebude kreslit v režimu ResizeColumnHeaderHeight:
            if (mouseMode != MouseMode.ResizeColumnHeaderHeight)
                _PaintHorizontalStandardLine(e);

            switch (mouseMode)
            {
                case MouseMode.ResizeColumnWidth:
                    _PaintVerticalResizeLine(e);
                    break;
                case MouseMode.ResizeColumnHeaderHeight:
                    _PaintHorizontalResizeLine(e);
                    break;

            }
        }
        /// <summary>
        /// Vykreslí svislou linku při resize ColumnWidth
        /// </summary>
        /// <param name="e"></param>
        private void _PaintVerticalResizeLine(PaintEventArgs e)
        {
            DColumn column = this._CurrentMouseStatus.ColumnActive;
            int x = column.Right;
            e.Graphics.DrawLine(SystemPens.ControlDarkDark, x - 2, 0, x - 2, this.Height);
            e.Graphics.DrawLine(SystemPens.ControlDark, x - 1, 0, x - 1, this.Height);
            e.Graphics.DrawLine(SystemPens.ControlDark, x + 0, 0, x + 0, this.Height);
            e.Graphics.DrawLine(SystemPens.ControlLight, x + 1, 0, x + 1, this.Height);
        }
        /// <summary>
        /// Vykreslí vodorovnou linku při resize ColumnHeaderHeight
        /// </summary>
        /// <param name="e"></param>
        private void _PaintHorizontalResizeLine(PaintEventArgs e)
        {
            int y = this.Height - 1;
            e.Graphics.DrawLine(SystemPens.ControlDarkDark, 0, y - 2, this.Width, y - 2);
            e.Graphics.DrawLine(SystemPens.ControlDark, 0, y - 1, this.Width, y - 1);
            e.Graphics.DrawLine(SystemPens.ControlDark, 0, y + 0, this.Width, y + 0);
            e.Graphics.DrawLine(SystemPens.ControlLight, 0, y + 1, this.Width, y + 1);
        }
        /// <summary>
        /// Vykreslí vodorovnou linku v běžném režimu, bez resize ColumnHeaderHeight
        /// </summary>
        /// <param name="e"></param>
        private void _PaintHorizontalStandardLine(PaintEventArgs e)
        {
            int y = this.Height - 2;
            e.Graphics.DrawLine(SystemPens.ControlDark, 0, y + 0, this.Width, y + 0);
            e.Graphics.DrawLine(SystemPens.ControlDarkDark, 0, y + 1, this.Width, y + 1);
        }
		#endregion
        #region ROW FILTER a SORT
        /// <summary>
        /// Obsahuje true, pokud kterýkoli sloupec (i z neviditelných) zobrazuje řádkový filtr
        /// </summary>
        internal bool ContainRowFilter
        {
            get
            {
                foreach (DColumn column in this.Columns)
                {
                    if (column.RowFilterVisible) return true;
                }
                return false;
            }
        }
        /// <summary>
        /// Nuluje řádkový filtr
        /// </summary>
        internal void RowFilterClear()
        {
            List<DColumn> columns = this.GetAllColumns(false);
            foreach (DColumn column in columns)
                column.RowFilterClear(true);
            OnColumnRowFilterChanged(this.SystemColumn);
        }
        /// <summary>
        /// Obsahuje true, pokud některý z viditelných sloupcu zobrazuje řádkový filtr
        /// </summary>
        internal bool ContainRowFilterInVisibleColumns
        {
            get
            {
                List<DColumn> columns = this.GetVisibleColumns(false);
                foreach (DColumn column in columns)
                {
                    if (column.RowFilterVisible) return true;
                }
                return false;
            }
        }
        /// <summary>
        /// Zajistí korektní umístění všech položek RowFilter
        /// </summary>
        private void _ColumnRowFilterReBounds()
        {
            this.SuspendLayout();

            foreach (DColumn column in this.Columns)
                column.RowFilterReBounds();

            this.ResumeLayout(false);
        }
        /// <summary>
        /// Obsahuje (vždy znovu načte) aktuální řádkový filtr.
        /// Pokud dojde ke změně filtru, bude vyvolán event this.RowFilterChanged
        /// </summary>
        public RowFilter RowFilter
        {
            get
            {
                RowFilter rowFilter = new RowFilter();
                List < DColumn > columns = this.GetVisibleColumns();
                foreach (DColumn column in columns)
                    rowFilter.Add(column.RowFilterItem);
                return rowFilter;
            }
        }
        /// <summary>
        /// Zajistí setřídění dat podle určeného sloupce, pro ostatní sloupce třídění zruší, zavolá OnColumnSortChanged() a Draw
        /// </summary>
        /// <param name="column">Sloupec, kde se má změnit SortMode</param>
        private void _SetSortByColumn(DColumn column)
        {
            if (!column.IsSortEnabled) return;
            ColumnSortMode mode = column.GetNextSortMode();
            this._SetSortByColumn(column, mode, true);
        }
        /// <summary>
        /// Zajistí setřídění dat podle určeného sloupce, pro ostatní sloupce třídění zruší, zavolá OnColumnSortChanged() a volitelně Draw
        /// </summary>
        /// <param name="column">Sloupec, kde se má změnit SortMode</param>
        /// <param name="mode">Cílový druh třídění</param>
        /// <param name="draw">Vyvolat Draw()</param>
        private void _SetSortByColumn(DColumn column, ColumnSortMode mode, bool draw)
        {
            bool isChange = (column.SortMode != mode);

            foreach (DColumn c in this.Columns)
            {   // Pro každý sloupec nastavím SortMode = None, kromě vstupního sloupce - kde nastavím režim třídění předem určený:
                ColumnSortMode m = (Object.ReferenceEquals(c, column) ? mode : ColumnSortMode.None);
                if (!isChange && c.SortMode != m)
                    isChange = true;
                c.SortMode = m;
            }

            // Event:
            if (isChange)
                this.OnColumnSortChanged(column);

            // Kreslit:
            if (draw)
                this.Draw();
        }
        #endregion
        #region HANDLERY VNITŘNÍCH UDÁLOSTÍ A VOLÁNÍ VNEJŠÍCH EVENTŮ
        internal virtual void OnColumnTextChanged(DColumn dColumn)
		{
            if (!this.SkipEvents && this.ColumnTextChanged != null)
                this.ColumnTextChanged(this, new DColumnEventArgs(dColumn));
            this.Draw();
        }
        internal virtual void OnColumnWidthChanging(DColumn dColumn)
        {
            if (!dColumn.ResizeWidthToFillParent)          // Pokud sloupec, který mění šířku (dColumn) není ten "gumový", pak podle aktuální šířky dopočteme šířku toho jiného gumového.
                this.RecalculateColumnWidthToFill(true, true);
            this._ColumnRowFilterReBounds();
            this._ColumnLinesInvalidate();

            //  Pořadí Draw() a handler změny:
            // V metodě Draw => Column.Paint() probíhá mimo jiné určení Bounds pro TimeAxis, tam se detekuje změna, 
            // provede se Resize pro TimeAxis, tím se určí nový TimeRange a vyvolá se event TimeRangeChanged.
            // Ten se přes synchronizer přenese do vedlejšího prvku, a je tedy vhodné, aby ten už měl aktuální synchronizovanou velikost (jako zdejší TimeAxis).
            // ale ten má dosud původní rozměr, takže se přepočte jeho měřítko času.
            // Je tedy vhodnější nejprve vyvolat event ColumnWidthChanged => Synchronizer => vedlejší DColumns.SychronizeColumnWidthFrom(),
            // a pak vyvolat překreslení (this.Draw()), odkud se vyvolá změna TimeR
            if (!this.SkipEvents && this.ColumnWidthChanging != null)
                this.ColumnWidthChanging(this, new DColumnWidthEventArgs(dColumn, this.ColumnWidthExport()));

            this.Draw();
        }
        internal virtual void OnColumnWidthChanged(DColumn dColumn)
		{
            if (!dColumn.ResizeWidthToFillParent)          // Pokud sloupec, který mění šířku (dColumn) není ten "gumový", pak podle aktuální šířky dopočteme šířku toho jiného gumového.
                this.RecalculateColumnWidthToFill(false, true);
            this._ColumnRowFilterReBounds();
            this._ColumnLinesInvalidate();

            //  Pořadí Draw() a handler změny:
            // V metodě Draw => Column.Paint() probíhá mimo jiné určení Bounds pro TimeAxis, tam se detekuje změna, 
            // provede se Resize pro TimeAxis, tím se určí nový TimeRange a vyvolá se event TimeRangeChanged.
            // Ten se přes synchronizer přenese do vedlejšího prvku, a je tedy vhodné, aby ten už měl aktuální synchronizovanou velikost (jako zdejší TimeAxis).
            // ale ten má dosud původní rozměr, takže se přepočte jeho měřítko času.
            // Je tedy vhodnější nejprve vyvolat event ColumnWidthChanged => Synchronizer => vedlejší DColumns.SychronizeColumnWidthFrom(),
            // a pak vyvolat překreslení (this.Draw()), odkud se vyvolá změna TimeR
            if (!this.SkipEvents && this.ColumnWidthChanged != null)
                this.ColumnWidthChanged(this, new DColumnWidthEventArgs(dColumn, this.ColumnWidthExport()));

            this.Draw();
        }
        /// <summary>
        /// V průběhu změny pozice Parent.Top + Height
        /// </summary>
		internal virtual void OnColumnHeaderParentMoving()
        {
            if (!this.SkipEvents && this.ColumnHeaderParentTopMoving != null)
				this.ColumnHeaderParentTopMoving(this, new EventArgs());
        }
		/// <summary>
		/// Po dokončení změny pozice Parent.Top + Height
		/// </summary>
		internal virtual void OnColumnHeaderParentMoved()
		{
            if (!this.SkipEvents && this.ColumnHeaderParentTopMoved != null)
				this.ColumnHeaderParentTopMoved(this, new EventArgs());
		}
		/// <summary>
        /// V průběhu změny pozice Top
        /// </summary>
        internal virtual void OnColumnHeaderTopChanging()
        {
            if (!this.SkipEvents && this.ColumnHeaderTopChanging != null)
                this.ColumnHeaderTopChanging(this, new EventArgs());
            this.Top = this.ColumnHeaderTopInteractive;
        }
        /// <summary>
        /// Po dokončení změny pozice Top
        /// </summary>
        internal virtual void OnColumnHeaderTopChanged()
        {
            if (!this.SkipEvents && this.ColumnHeaderTopChanged != null)
                this.ColumnHeaderTopChanged(this, new EventArgs());
            this.Top = this.ColumnHeaderTop;
        }
        /// <summary>
        /// V průběhu změny výšky Header
        /// </summary>
        internal virtual void OnColumnHeaderHeightChanging()
        {
            this.Height = this.ColumnHeaderHeightInteractive;
            this.Draw();
            if (!this.SkipEvents && this.ColumnHeaderHeightChanging != null)
                this.ColumnHeaderHeightChanging(this, new EventArgs());
        }
        /// <summary>
        /// Po dokončení změny výšky Header
        /// </summary>
        internal virtual void OnColumnHeaderHeightChanged()
        {
			this.Height = this.ColumnHeaderHeight;
			this.Draw();
            if (!this.SkipEvents && this.ColumnHeaderHeightChanged != null)
                this.ColumnHeaderHeightChanged(this, new EventArgs());
        }
        internal void OnColumnHeaderModeChanged(DColumn dColumn)
        {
            if (!this.SkipEvents && this.ColumnHeaderModeChanged != null)
                this.ColumnHeaderModeChanged(this, new DColumnEventArgs(dColumn));
            this.Draw();
        }
        internal virtual void OnColumnVisibleChanged(DColumn dColumn)
		{
            this._ColumnLinesInvalidate();
            if (!this.SkipEvents && this.ColumnVisibleChanged != null)
                this.ColumnVisibleChanged(this, new DColumnEventArgs(dColumn));
            this.Draw();
        }
        internal virtual void OnColumnOrderChanged(DColumn dColumn)
		{
            this._ColumnLinesInvalidate();
            if (!this.SkipEvents && this.ColumnOrderChanged != null)
                this.ColumnOrderChanged(this, new DColumnEventArgs(dColumn));
            this.Draw();
        }
        /// <summary>
        /// Po změně počtu sloupců (Add, Remove, Clear)
        /// </summary>
        internal virtual void OnColumnCountChanged()
        {
            this._ColumnLinesInvalidate();
            if (!this.SkipEvents && this.ColumnCountChanged != null)
                this.ColumnCountChanged(this, new EventArgs());
            this.Draw();
        }
        internal virtual void OnMouseChanged()
        {
            this.Draw();
        }
        internal virtual void OnColumnRowFilterChanged(DColumn dColumn)
        {
            if (!this.SkipEvents && this.RowFilterChanged != null)
                this.RowFilterChanged(this, new DColumnRowFilterEventArgs(dColumn, this.RowFilter));
        }
        internal void OnColumnSortChanged(DColumn dColumn)
        {
            if (!this.SkipEvents && this.ColumnSortChanged != null)
                this.ColumnSortChanged(this, new DColumnEventArgs(dColumn));
        }
        #endregion
        #region TIMEAXIS (ČASOVÁ OSA) : ULOŽENÍ INSTANCE, PŘÍPRAVA, PROPERTY, EVENTY
        /// <summary>
        /// Reference na objekt TimeAxis. Pokud v Columns není přítomen sloupec TimeAxis (viz property TimeAxisRequired), pak obsahuje null.
        /// Pokud dosud nebyla časová osa připravena, nepřipravuje se! Pak obsahuje null.
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Browsable(false)]
        public TimeAxisCls TimeAxis
        {
            get
            {
                if (TimeAxisRequired)
                    return _TimeAxis;
                return null;
            }
        }
        /// <summary>
        /// Příznak, že aktuální Columns předepisují přítomnost časové osy (některý column má HeaderMode == DColumnHeaderMode.TimeAxis).
        /// Vrací true i tehdy, když sloupec TimeAxis je momentálně neviditelný (Visible = false).
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Browsable(false)]
        public bool TimeAxisRequired
        {
            get
            {
                foreach (DColumn col in this.Columns)
                {
                    if (col.HeaderMode == DColumnHeaderMode.TimeAxis)
                        return true;
                }
                return false;
            }
        }
        /// <summary>
        /// Pokud má být zobrazena časová osa, pak ji umístí na korektní pozici (vypočte).
        /// Případně potlačí event TimeAxisChange
        /// </summary>
        /// <param name="suppressEventTimeAxisChange"></param>
        private void _TimeAxisSetBounds(bool suppressEventTimeAxisChange)
        {
            if (!this.TimeAxisRequired) return;
            this._CheckPrepareTimeAxis();
            Rectangle timeAxisBounds = _TimeAxisDetectBounds();
            if (timeAxisBounds == Rectangle.Empty) return;
            if (this._TimeAxis.Bounds == timeAxisBounds) return;

            bool oldSup = this._TimeAxis.SuppressOnChangeEvent;
            this._TimeAxis.SuppressOnChangeEvent = suppressEventTimeAxisChange;
            this._TimeAxis.Bounds = timeAxisBounds;
            this._TimeAxis.SuppressOnChangeEvent = oldSup;
        }
        /// <summary>
        /// Určí aktuální pozici časové osy
        /// </summary>
        /// <returns></returns>
        private Rectangle _TimeAxisDetectBounds()
        {
            List<DColumn> columns = this.GetVisibleColumns(true, true);
            DColumn timeAxisColumn = columns.Find(c => c.HeaderMode == DColumnHeaderMode.TimeAxis);
            if (timeAxisColumn == null) return Rectangle.Empty;
            return timeAxisColumn.HeaderBounds;
        }

        /// <summary>
        /// Prověří, zda má být připravena časová osa, a pokud má být, pak jestli je, a pokud není, tak ji vytvoří.
        /// Prověřuje to jen pro daný sloupec.
        /// </summary>
        /// <param name="dColumn"></param>
        public void CheckPrepareTimeAxis(DColumn dColumn)
        {
            if (dColumn.HeaderMode != DColumnHeaderMode.TimeAxis) return;
            this._CheckPrepareTimeAxis();
        }
        /// <summary>
        /// Prověří, zda má být připravena časová osa, a pokud má být, pak jestli je, a pokud není, tak ji vytvoří.
        /// Prověřuje to pro všechny sloupce.
        /// </summary>
        /// <param name="dColumn"></param>
        public void CheckPrepareTimeAxis()
        {
            if (!this.TimeAxisRequired) return;
            this._CheckPrepareTimeAxis();
        }
        /// <summary>
		/// Pokud dosud neexistuje _TimeAxis, připraví ji.
		/// </summary>
		private void _CheckPrepareTimeAxis()
		{
            if (this._TimeAxis != null) return;
            this._TimeAxisCreate();
        }
        /// <summary>
        /// Vytvoří TimeAxis
        /// </summary>
        private void _TimeAxisCreate()
        {
            this._TimeAxis = new TimeAxisCls() { Visible = false };
            this.Controls.Add(this._TimeAxis);
            this._TimeAxis.Location = new Point(150, 0);
            this._TimeAxis.Size = new Size(150, 42);
            this._TimeAxis.AxisChange += new TimeAxisChangeHandler(OnTimeAxisChange);
            this._TimeAxis.Visible = false;
        }
        /// <summary>
        /// Instance časové osy
        /// </summary>
        private TimeAxisCls _TimeAxis;
        /// <summary>
        /// { get; set } Vizuální rozsah časové osy
        /// </summary>
        [Browsable(true)]
        [Category("Action")]
        [Description("Vizuální rozsah časové osy")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public TimeRange CurrentTimeRange
        {
            get { return this._TimeAxis.AxisVisibleTimeRange; }
            set { this._TimeAxis.AxisVisibleTimeRange = value; }
        }
        /// <summary>
        /// Aktuální seznam ticků osy.
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Browsable(false)]
        public List<TimeAxisTickInfo> AxisTicks
        {
            get
            {
                if (this._TimeAxis != null)
                    return this._TimeAxis.AxisTicks;
                return null;
            }
        }
        /// <summary>
        /// Reference na živý objekt časového kalkulátoru (aktuální rozsah, měřítko, vizuální přepočty)
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Browsable(false)]
        public ITimeAxisCalc TimeAxisCalc { get { this._CheckPrepareTimeAxis(); return this._TimeAxis.TimeAxisCalc; } }
        /// <summary>
        /// Event, který bude vyvolán při změnách zobrazeného času na časové ose
        /// </summary>
        [Browsable(true)]
        [Category("Action")]
        [Description("Event, který bude vyvolán při změnách zobrazeného času na časové ose")]
        public event TimeAxisChangeHandler TimeAxisChange;
        protected void OnTimeAxisChange(object sender, TimeAxisChangeEventArgs e)
        {
			// Pozor, tady dochází ke změně v senderovi: původní sender je TimeAxis, ale odsud se předává dál this = DColumns:
            if (!this.SkipEvents && this.TimeAxisChange != null)
                this.TimeAxisChange(this, e);
        }
        /// <summary>
        /// Nastaví vlastnosti časové osy.
        /// Je voláno zvenku, nastaví časovou osu, a jedenkrát vyvolá event OnTimeAxisChange.
        /// </summary>
        /// <param name="e">Data pro požadovanou změnu</param>
        public void SetTimeAxisData(TimeAxisSetAxisArgs e)
        {
            this._TimeAxis.SetTimeAxisData(e);
        }
        /// <summary>
        /// Zastaví případný pohyb na časové ose
        /// </summary>
        public void StopTimeAxisMove()
        {
            this._TimeAxis.InertialMoveStop();
        }
        /// <summary>
        /// Zajistí korektní umístění časové osy do daného prostoru
        /// </summary>
        /// <param name="axisBound">Prostor pro umístění časové osy</param>
        /// <param name="suppressTimeChangeEvent">true = potlačit event OnTimeChange, ke kterému by zajisté došlo (provede se TimeAxis.Resize, což změní vizuální prostor).</param>
        internal void TimeAxisSetBounds(Rectangle axisBound, bool suppressTimeChangeEvent)
        {
            if (this._TimeAxis == null) return;
            if (this._TimeAxis.Bounds == axisBound) return;

            try
            {
                this._TimeAxis.SuppressOnChangeEvent = suppressTimeChangeEvent;

                this._TimeAxis.Bounds = axisBound;
                if (axisBound.Width > 0 && axisBound.Height > 0)
                {
                    if (!this._TimeAxis.Visible)
                        this._TimeAxis.Visible = true;
                }
                else
                {
                    if (this._TimeAxis.Visible)
                        this._TimeAxis.Visible = false;
                }
            }
            finally
            {
                this._TimeAxis.SuppressOnChangeEvent = false;
            }
        }
        #endregion
        #region RESIZE ŠÍŘKY SLOUPCŮ TAK, ABY SE NAPLNILA CELÁ ŠÍŘKA this.Width
        /// <summary>
        /// Nastavit false do všech sloupců do property ResizeWidthToFillParent
        /// </summary>
        /// <param name="column"></param>
        internal void ResetResizeWidthToFillParentExcept()
        {
            foreach (DColumn column in this.Columns)
                column.ResizeWidthToFillParent = false;
        }
        /// <summary>
        /// Obsahuje true, pokud this obsahuje sloupec, který je určen k vyplnění šířky
        /// </summary>
        internal bool ContainColumnToFillWidth
        {
            get
            {
                return this.Columns.Exists(c => c.ResizeWidthToFillParent);
            }
        }
        /// <summary>
        /// Obsahuje true, pokud this obsahuje sloupec, který je určen k vyplnění šířky, a je nyní viditelný
        /// </summary>
        internal bool ContainVisibleColumnToFillWidth
        {
            get
            {
                return this.Columns.Exists(c => c.ResizeWidthToFillParent && c.Visible);
            }
        }
        /// <summary>
        /// Najde sloupec, který má vlastnost ResizeWidthToFillParent = true, a nastaví jeho šířku tak, aby byly plně vidět všechny potřebné sloupce.
        /// </summary>
        internal void RecalculateColumnWidthToFill()
        {
            this.RecalculateColumnWidthToFill(false, false);
        }
        /// <summary>
        /// Najde sloupec, který má vlastnost ResizeWidthToFillParent = true, a nastaví jeho šířku tak, aby byly plně vidět všechny potřebné sloupce.
        /// </summary>
        internal void RecalculateColumnWidthToFill(bool interactive, bool suppressEventsAndDraw)
        {
            if (_RecalculatingWidthToFill) return;
            bool skipEvents = this.SkipEvents;
            try
            {
                _RecalculatingWidthToFill = true;

                DColumn rwCol = this.Columns.Find(c => c.ResizeWidthToFillParent && c.Visible);
                if (rwCol == null) return;                        // Žádný sloupec to nechce dělat (anebo není vidět): skončím.

                // Pro všechny aktuálně viditelné sloupce:
                List<DColumn> columns = GetVisibleColumns(true);
                int columnsCount = columns.Count;
                if (columnsCount == 0) return;                    // Paranoia: alespoň jeden sloupec je viditelný (ten náš rwCol).
                int rightC = columns[columnsCount - 1].Right;     // Kde končíme nyní (Right posledního viditelného sloupce)

                // Kde má končit poslední sloupec:
                int rightN = this.Width - 1;
                if (rightC == rightN) return;                     // OK?

                // Nastavím šířku našeho sloupce (upravím ji o tolik, aby to sedlo):
                int newWidth = rwCol.VisibleWidth + (rightN - rightC);

                // Potlačit eventy (na konci metody ve finally bloku se hodnota vrátí):
                if (suppressEventsAndDraw)
                    this.SkipEvents = true;

                // Interaktivní / Neinteraktivní režim:
                // Nemusím testovat změnu hodnoty, to testuje set accessor a případně volá eventy:
                if (interactive)
                    rwCol.WidthInteractive = newWidth;
                else
                    rwCol.Width = newWidth;
            }
            finally
            {
                this.SkipEvents = skipEvents;
                this._RecalculatingWidthToFill = false;
            }
        }
        /// <summary>
        /// Příznak, že právě nyní probíhá přepočet šířky sloupce s vlastností "ResizeWidthToFillParent".
        /// Pokud je _RecalculatingWidth == true, pak se metoda RecalculateColumnWidthToFill() nebude nic dělat (protože už běží).
        /// </summary>
        private bool _RecalculatingWidthToFill;
        #endregion
		#region PODPORA SYNCHRONIZACE DAT MEZI VÍCE INSTANCEMI DColumns
		/// <summary>
		/// Objekt Synchronizer, který zajišťuje synchronizaci předepsaných vlastností mezi vícero objekty DColumns.
		/// Synchronizer se vytvoří (jedenkrát jako new DColumnsSynchronizer()), a pak se vloží do všech DColumns, které se mají navzájem synchronizovat.
		/// Při ukončení se do property this.Synchronizer vloží null, tím se DColumns odváže ze synchronizace.
		/// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Browsable(false)]
        public DColumnsSynchronizer Synchronizer
		{
			get { return this._Synchronizer; }
			set
			{
				if (value == null)
				{	// Odpojuji se ze synchronizace:
					if (this._Synchronizer != null)
						this._Synchronizer.Unlist(this);
					this._Synchronizer = null;
				}
				else
				{	// Zapojuji se do synchronizace:
					// Pokud mám referenci na nějaký (starý) synchronizer, tak se z něj nejdřív odpojím:
					if (this._Synchronizer != null)
						this._Synchronizer.Unlist(this);

					// Zapojím se do nově předaného synchronizeru:
					this._Synchronizer = value;
					this._Synchronizer.Enlist(this);
				}
			}
		}
        /// <summary>
        /// Nastaví výšku Header podle výšky dodaného objektu
        /// </summary>
        /// <param name="source">Zdroj hodnot, jiná instance téže třídy</param>
        /// <param name="args">Data změny</param>
        /// <param name="interactive">Interaktivní režim?</param>
        internal void SychronizeColumnHeightFrom(IHeaderSync source, EventArgs args, bool interactive)
        {
            try
            {
                this.SkipEvents = true;            // Po dobu běhu této metody nebudeme vyvolávat naše standardní eventy, jen na závěr provedu Draw()

                if (interactive)
                    this.ColumnHeaderHeightInteractive = source.ColumnHeaderHeightInteractive;
                else
                    this.ColumnHeaderHeight = source.ColumnHeaderHeight;
            }
            finally
            {
                this.SkipEvents = false;
                this.Draw();
            }
        }
		/// <summary>
		/// Nastaví šířku this sloupců podle šířky dodaných sloupců
		/// </summary>
        /// <param name="source">Zdroj hodnot, jiná instance téže třídy</param>
        /// <param name="args">Data změny</param>
        /// <param name="interactive">Interaktivní režim?</param>
        internal void SychronizeColumnWidthFrom(IHeaderSync source, DColumnWidthEventArgs args, bool interactive)
		{
			try
			{
				this.SkipEvents = true;            // Po dobu běhu této metody nebudeme vyvolávat naše standardní eventy, jen na závěr provedu Draw()

				int cnt = Math.Min(this.ColumnsCount, source.ColumnsCount);
				for (int idx = 0; idx < cnt; idx++)
				{
					if (interactive)
						this.Columns[idx].WidthInteractive = source.Columns[idx].WidthInteractive;
					else
						this.Columns[idx].Width = source.Columns[idx].Width;
				}
			}
			finally
			{
				this.SkipEvents = false;
				this.Draw();
			}
		}
        /// <summary>
        /// Nastaví hodnotu Visible this sloupců podle hodnoty z dodaných sloupců
        /// </summary>
        /// <param name="source">Zdroj hodnot, jiná instance téže třídy</param>
        /// <param name="args">Data změny</param>
        /// <param name="interactive">Interaktivní režim?</param>
        internal void SychronizeColumnVisibleFrom(IHeaderSync source, DColumnEventArgs args, bool interactive)
        {
            try
            {
                this.SkipEvents = true;            // Po dobu běhu této metody nebudeme vyvolávat naše standardní eventy, jen na závěr provedu Draw()

                int cnt = Math.Min(this.ColumnsCount, source.ColumnsCount);
                for (int idx = 0; idx < cnt; idx++)
                    this.Columns[idx].Visible = source.Columns[idx].Visible;
            }
            finally
            {
                this.SkipEvents = false;
                this.Draw();
            }
        }
        /// <summary>
        /// Nastaví hodnotu ColumnOrder this sloupců podle hodnoty z dodaných sloupců
        /// </summary>
        /// <param name="source">Zdroj hodnot, jiná instance téže třídy</param>
        /// <param name="args">Data změny</param>
        /// <param name="interactive">Interaktivní režim?</param>
        internal void SychronizeColumnOrderFrom(IHeaderSync source, DColumnEventArgs args, bool interactive)
        {
            try
            {
                this.SkipEvents = true;            // Po dobu běhu této metody nebudeme vyvolávat naše standardní eventy, jen na závěr provedu Draw()

                int cnt = Math.Min(this.ColumnsCount, source.ColumnsCount);
                for (int idx = 0; idx < cnt; idx++)
                    this.Columns[idx].ColumnOrderInternal = source.Columns[idx].ColumnOrderInternal;
                this.ColumnOrderRecalc();
            }
            finally
            {
                this.SkipEvents = false;
                this.Draw();
            }
        }
        /// <summary>
        /// Nastaví hodnotu SortMode this sloupců podle hodnoty z dodaných sloupců
        /// </summary>
        /// <param name="source">Zdroj hodnot, jiná instance téže třídy</param>
        /// <param name="args">Data změny</param>
        /// <param name="interactive">Interaktivní režim?</param>
        internal void SychronizeColumnSortFrom(IHeaderSync source, DColumnEventArgs args, bool interactive)
        {
            try
            {
                this.SkipEvents = true;            // Po dobu běhu této metody nebudeme vyvolávat naše standardní eventy, jen na závěr provedu Draw()

                int cnt = Math.Min(this.ColumnsCount, source.ColumnsCount);
                for (int idx = 0; idx < cnt; idx++)
                    this.Columns[idx].SortMode = source.Columns[idx].SortMode;
            }
            finally
            {
                this.SkipEvents = false;
                this.Draw();
            }
        }
		/// <summary>
		/// Nastaví časové rozmezí časové osy this podle dat dodaného objektu
		/// </summary>
		/// <param name="source">Zdroj hodnot, jiná instance téže třídy</param>
		/// <param name="args">Data změny</param>
        /// <param name="interactive">Interaktivní režim?</param>
        internal void SychronizeTimeAxisRangeFrom(IHeaderSync source, TimeAxisChangeEventArgs args, bool interactive)
		{
			try
			{
				this.SkipEvents = true;            // Po dobu běhu této metody nebudeme vyvolávat naše standardní eventy, jen na závěr provedu Draw()

                TimeAxisCls targetTimeAxis = this.TimeAxis;
                
            //     a) podle source grafu:
            //  TimeAxisCls sourceTimeAxis = source.TimeAxis;
            //  if (targetTimeAxis != null && sourceTimeAxis != null)
            //      targetTimeAxis.AxisVisibleTimeRange = sourceTimeAxis.AxisVisibleTimeRange;

                // b) podle dat v argumentu:
                if (targetTimeAxis != null)
                    targetTimeAxis.AxisVisibleTimeRange = args.TimeRange; // sourceTimeAxis.AxisVisibleTimeRange;

			}
			finally
			{
				this.SkipEvents = false;
				this.Draw();
			}
		}
		#endregion
        #region IHeaderSync Members

        List<TimeAxisTickInfo> IHeaderSync.AxisTicks { get { return this.AxisTicks; } }
        TimeRange IHeaderSync.CurrentTimeRange { get { return this.CurrentTimeRange; } set { this.CurrentTimeRange = value; } }
        ITimeAxisCalc IHeaderSync.TimeAxisCalc { get { return this.TimeAxisCalc; } }
        int IHeaderSync.ColumnHeaderHeightInteractive { get { return this.ColumnHeaderHeightInteractive; } }
        int IHeaderSync.ColumnHeaderHeight { get { return this.ColumnHeaderHeight; } }
        List<DColumn> IHeaderSync.Columns { get { return this.Columns; } }
        int IHeaderSync.ColumnsCount { get { return this.ColumnsCount; } }

        event EventHandler IHeaderSync.ColumnHeaderHeightChanging
        {
            add { this.ColumnHeaderHeightChanging += value; }
            remove { this.ColumnHeaderHeightChanging -= value; }
        }
        event EventHandler IHeaderSync.ColumnHeaderHeightChanged
        {
            add { this.ColumnHeaderHeightChanged += value; }
            remove { this.ColumnHeaderHeightChanged -= value; }
        }
        event DColumnWidthEventHandler IHeaderSync.ColumnWidthChanging
        {
            add { this.ColumnWidthChanging += value; }
            remove { this.ColumnWidthChanging -= value; }
        }
        event DColumnWidthEventHandler IHeaderSync.ColumnWidthChanged
        {
            add { this.ColumnWidthChanged += value; }
            remove { this.ColumnWidthChanged -= value; }
        }
        event DColumnEventHandler IHeaderSync.ColumnVisibleChanged
        {
            add { this.ColumnVisibleChanged += value; }
            remove { this.ColumnVisibleChanged -= value; }
        }
        event TimeAxisChangeHandler IHeaderSync.TimeAxisChange
        {
            add { this.TimeAxisChange += value; }
            remove { this.TimeAxisChange -= value; }
        }
        event DColumnEventHandler IHeaderSync.ColumnSortChanged
        {
            add { this.ColumnSortChanged += value; }
            remove { this.ColumnSortChanged -= value; }
        }
        event DColumnRowFilterEventHandler IHeaderSync.RowFilterChanged
        {
            add { this.RowFilterChanged += value; }
            remove { this.RowFilterChanged -= value; }
        }

        void IHeaderSync.SychronizeColumnHeightFrom(IHeaderSync source, EventArgs args, bool interactive)
        {
            this.SychronizeColumnHeightFrom(source, args, interactive);
        }
        void IHeaderSync.SychronizeColumnWidthFrom(IHeaderSync source, DColumnWidthEventArgs args, bool interactive)
        {
            this.SychronizeColumnWidthFrom(source, args, interactive);
        }
        void IHeaderSync.SychronizeColumnVisibleFrom(IHeaderSync source, DColumnEventArgs args, bool interactive)
        {
            this.SychronizeColumnVisibleFrom(source, args, interactive);
        }
        void IHeaderSync.SychronizeColumnOrderFrom(IHeaderSync source, DColumnEventArgs args, bool interactive)
        {
            this.SychronizeColumnOrderFrom(source, args, interactive);
        }
        void IHeaderSync.SychronizeColumnSortFrom(IHeaderSync source, DColumnEventArgs args, bool interactive)
        {
            this.SychronizeColumnSortFrom(source, args, interactive);
        }
        void IHeaderSync.SychronizeTimeAxisRangeFrom(IHeaderSync source, TimeAxisChangeEventArgs args, bool interactive)
        {
            this.SychronizeTimeAxisRangeFrom(source, args, interactive);
        }

        void IHeaderSync.SuppressDrawingPush() { this.SuppressDrawingPush(); }
        void IHeaderSync.SuppressDrawingPop() { this.SuppressDrawingPop(); }
        bool IHeaderSync.SuppressDrawing { get { return this.SuppressDrawing; } set { this.SuppressDrawing = value; } }
        void IHeaderSync.SetTimeAxisData(TimeAxisSetAxisArgs e) { this.SetTimeAxisData(e); }
        void IHeaderSync.StopTimeAxisMove() { this.StopTimeAxisMove(); }

        #endregion
    }
	#endregion
    #region CLASS DColumn : data jednoho sloupce. Nemají vlastní eventy, volá napřímo odpovídající metody Parenta, který řeší externí eventy.
    /// <summary>
    /// DColumn : data jednoho sloupce
    /// </summary>
	public class DColumn
	{
		#region KONSTRUKCE, PROMĚNNÉ
		internal DColumn(DColumns parent, string key)
		{
			this._Parent = parent;
			this._Key = key;
			this._InitValues();
		}
		internal DColumn(DColumns parent, string key, string text)
		{
            this._Parent = parent;
			this._Key = key;
            this._InitValues();
            this._Text = text;
		}
        internal DColumn(DColumns parent, string text, int width)
        {
            this._Parent = parent;
            this._InitValues();
            this._Text = text;
            this._Width = width;
            this._WidthInteractive = width;
        }
        private DColumn(DColumns parent, string text, int width, bool isSystemColumn)
        {
            this._Parent = parent;
            this._IsSystemColumn = isSystemColumn;
            this._InitValues();
            this._Text = text;
            this._Width = width;
            this._WidthInteractive = width;
        }
        internal DColumn(DColumns parent, string key, string text, int width)
		{
            this._Parent = parent;
			this._Key = key;
			this._InitValues();
			this._Text = text;
			this._Width = width;
            this._WidthInteractive = width;
		}
        /// <summary>
        /// Vytvoří a vrátí systémový column
        /// </summary>
        /// <param name="parent"></param>
        /// <returns></returns>
        internal static DColumn CreateSystemColumn(DColumns parent)
        {
            DColumn column = new DColumn(parent, "", 30, true);
            column._RowFilterVisible = false;
            return column;
        }
        /// <summary>
        /// Inicializuje hodnoty. Musí být voláno až poté, kdy je přiřazena reference this.Parent !!!
        /// </summary>
		private void _InitValues()
		{
            this._Width = 150;
            this._Visible = true;
            this._InnerPadding = new Padding(2, 2, 2, 3);
            this._Alignment = ContentAlignment.MiddleLeft;
            this._Index = this.Parent.ColumnsCount - 1;            // První se vždy zakládá SystemColumn, automaticky. Ten má index -1 (i ColumnOrder). Standardní datové columny mají index 0 a vyšší.
			this._ColumnOrder = this._Index;
            this._IsMovable = true;
            this._IsResizable = true;
            this._Enabled = true;
            this._HeaderMode = DColumnHeaderMode.StandardHeader;
            this._SortMode = ColumnSortMode.None;

            if (this.IsSystemColumn)
                this._FilterClearCreate();
		}
		private DColumns _Parent;
		private string _Key;
		private int _Index;
        private bool _IsSystemColumn;
        private bool _RowFilterVisible;
        private DColumnHeaderMode _HeaderMode;
        private string _RowFilterItemName;
        /// <summary>
        /// Control, který představuje vstupní prvek pro zadání řádkového filtru
        /// </summary>
        private DRowFilterItem _FilterItem;
        /// <summary>
        /// Control, který obsahuje Button, který zajistí Clear() na celý filtr.
        /// Objekt je umístěn pouze na systémovém columnu.
        /// Vytváří se ihned při vytváření columnu, protože už v tu dobu je znám Parent objekt (=Control), do něhož se Clear přidává (aby vizuálně fungoval).
        /// Viz metoda _InitValues() -» _FilterClearCreate().
        /// </summary>
        private DRowFilterClear _FilterClear;
		private int _ColumnOrder;
		private string _Text;
		private bool _Visible;
        private Padding _InnerPadding;
        private bool _Enabled;
        private bool _ResizeWidthToFillParent;
		private int _Width;
        private int _WidthInteractive;
        private int? _MinimumWidth;
        private int? _MaximumWidth;
        private bool _IsResizable;
        private bool _IsMovable;
        private bool _IsSortEnabled;
        private ColumnSortMode _SortMode;
        private ContentAlignment _Alignment;
        public override string ToString()
        {
            return "[" + this.Index.ToString() + "] " + this.Text + " (" + this.RowFilterItemName + ")";
        }
        /// <summary>
        /// Komparátor pro třídění dle vizuálního pořadí
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
		public static int CompareByOrder(DColumn a, DColumn b)
		{
			// Systémový sloupec nejdřív:
            if (a.IsSystemColumn && !b.IsSystemColumn) return -1;
            if (!a.IsSystemColumn && b.IsSystemColumn) return 1;
            // Standardní comparer:
            int cmp = a.ColumnOrder.CompareTo(b.ColumnOrder);
			if (cmp != 0) return cmp;
			cmp = a.Index.CompareTo(b.Index);
			return cmp;
		}
		#endregion
		#region PROPERTY
        /// <summary>
        /// Reference na kolekci Columns, která mě hostí.
        /// </summary>
		public DColumns Parent
		{
			get { return this._Parent; } 
		}
        /// <summary>
        /// Klíč sloupce. Pokud je zadaný, lze podle něj sloupec identifikovat (najít). Nesmí být duplicitní.
        /// </summary>
		public string Key
		{
			get { return this._Key; } 
		}
        /// <summary>
        /// Příznak, že jde o systémový sloupec (má index = -1).
        /// </summary>
        public bool IsSystemColumn
        {
            get { return this._IsSystemColumn;}
        }
        /// <summary>
        /// Příznak, že pro daný column má být zobrazen řádkový filtr
        /// </summary>
        public bool RowFilterVisible
        {
            get { return this._RowFilterVisible; }
            set { _RowFilterSetVisible(value); }
        }
        /// <summary>
        /// Režim zobrazení záhlaví sloupce. Výchozí hodnota je DColumnHeaderMode.StandardHeader.
        /// Lze nastavit režim Časová osa, tento režim smí být použit nanejvýše u jednoho sloupce.
        /// Objekt časové osy je dostupný v property DColumns.TimeAxis.
        /// Pod časovou osou může/nemusí být zobrazen řádkový filtr, záleží na property DColumn.RowFilterVisible
        /// </summary>
        public DColumnHeaderMode HeaderMode
        {
            get { return this._HeaderMode; }
            set
            {
                if (value != this._HeaderMode) ;
                {
                    this._HeaderMode = value;
                    this._OnHeaderModeChanged();
                }
            }
        }
        /// <summary>
        /// Klíčový název položky v tomto sloupci. Tento název se bude objevovat v this.RowFilterItem v property ItemName. Typicky obsahuje název hodnoty, která je v tomto sloupci zobrazována.
        /// Pokud není zadaná hodnota, vrací this.Key.
        /// </summary>
        public string RowFilterItemName
        {
            get
            {
                if (!String.IsNullOrEmpty(this._RowFilterItemName))
                    return this._RowFilterItemName;
                return this.Key;
            }
            set { this._RowFilterItemName = value; }
        }
        /// <summary>
        /// Položka řádkového filtru v tomto sloupci. Pokud neexistuje, vrací se null.
        /// </summary>
        public RowFilterItem RowFilterItem
        {
            get
            {
                if (this.VisibleWidth <= 0 || !this.RowFilterVisible || this._FilterItem == null)
                    return null;
                return this._FilterItem.RowFilterItem;
            }
        }
        /// <summary>
        /// Obsahuje true, pokud tento řádkový filtr má nyní focus (bliká kurzor v textboxu)
        /// </summary>
        public bool RowFilterHasFocus
        {
            get
            {
                if (this.VisibleWidth <= 0 || !this.RowFilterVisible || this._FilterItem == null)
                    return false;
                return this._FilterItem.Focused;
            }
        }
        /// <summary>
        /// Text v ColumnHeader. Může obsahovat NewLine.
        /// </summary>
		public string Text
		{
			get { return this._Text; }
			set
			{
				if (!String.Equals(this._Text, value))
				{
                    this._Text = value;
   					this._OnTextChanged();
				}
			}
		}
        /// <summary>
        /// Index tohoto sloupce. Index je neměnný, a je daný pořadím, v němž jsou columny přidávány do kolekce Columns.
        /// </summary>
		public int Index
		{
			get { return this._Index; }
		}
        /// <summary>
        /// Pořadí tohoto sloupce. Změna pořadí přepočte pořadí okolních sloupců a vyvolá event 
        /// </summary>
		public int ColumnOrder
		{
			get { return this._ColumnOrder; }
            set
            {
                if (this._ColumnOrder != value)
                {
                    this._ColumnOrderSet(value);
                    if (this._FilterItem != null)
                        this._FilterItem.TabIndex = this._ColumnOrder;
                    this._OnOrderChanged();
                }
            }
        }
        /// <summary>
        /// Pořadí tohoto sloupce. Změna pořadí v této property neprovede přepočet pořadí okolních sloupců ani vyvolá event!!!
        /// Poté je třeba volat metodu this.Parent.ColumnOrderRecalc(), která zajistí unikátnost a posloupnost všech ColumnOrder.
        /// </summary>
        internal int ColumnOrderInternal
        {
            get { return this._ColumnOrder; }
            set { this._ColumnOrder = value; }
        }
        /// <summary>
        /// Zajistí, že pořadí (ColumnOrder) tohoto sloupce bude dle dané hodnoty, a pro ostatní sloupce se přiměřeně přečísluje jejich ColumnOrder.
        /// </summary>
        /// <param name="value"></param>
        private void _ColumnOrderSet(int value)
        {
            if (this.IsSystemColumn) return;                              // Systémový sloupec nelze přeřadit jinam !

            // Změna pořadí se musí odehrát zde, abych mohl zapisovat nové pořadí do DColumn._ColumnOrder (private field) a obejít tak zdejší set accessor (v public property).
            //  (Ten by totiž vyvolal tuto logiku rekurzivně až do úplného vysílení zásobníku).
            List<DColumn> columns = this.Parent.GetAllColumns(true);      // Sloupce v dosavadním pořadí (podle bývalého SortOrder), včetně systémového sloupce (ten je na fyzickém indexu [0])
            int columnCount = columns.Count;
            int orderThis = (value < 0 ? 0 : value > (columnCount - 2) ? (columnCount - 2) : value);   // Nová hodnota ColumnOrder do tohoto sloupce, smí být nejvýše (Count - 2)
            int orderPrev = this._ColumnOrder;                                                         // Původní hodnota ColumnOrder tohoto sloupce
            if (orderThis == orderPrev) return;                           // Pokud by nedošlo ke změně, nic neřešíme.

            bool moveUp = (orderThis > orderPrev);                        // true = order se posouvá nahoru, jiný algoritmus
            for (int c = 1; c < columnCount; c++)                         // Index běží od 1, protože prvek [0] je systémový, a nebudeme jej měnit.
            {
                DColumn column = columns[c];
                if (Object.ReferenceEquals(column, this))
                    // Zde vkládám požadovanou hodnotu ColumnOrder do this:
                    column._ColumnOrder = orderThis;
                else if (moveUp)
                {   // Prvek this posouvám nahoru (nová hodnota ColumnOrder je větší než původní hodnota),
                    //  co udělám s ostatními prvky?
                    if (c < orderPrev)
                        // Prvky předcházející před původní pozicí tohoto prvku se nezmění:
                        column._ColumnOrder = c;
                    else if (c <= orderThis)
                        // Prvky, které leží za původní pozicí this.ColumnOrder, ale před (nebo do) novou pozicí this.ColumnOrder, se musí posunout o 1 doleva
                        //  (obsadí tak uvolněnou pozici, která zbyde po odsunutí prvku this nahoru):
                        column._ColumnOrder = c - 1;
                    else
                        // Prvky, které leží za novou pozicí, budou mít pořadí odpovídající původní pozici v seznamu:
                        column._ColumnOrder = c;
                }
                else
                {   // Prvek this posouvám dolů (nová hodnota ColumnOrder je menší než původní hodnota),
                    //  co udělám s ostatními prvky?
                    if (c < orderThis)
                        // Prvky předcházející před novou pozicí tohoto prvku se nezmění:
                        column._ColumnOrder = c;
                    else if (c <= orderPrev)
                        // Prvky, které leží před nebo na původní pozici this.ColumnOrder, ale za novou pozicí this.ColumnOrder, se musí posunout o 1 doprava
                        //  (uvolní tak vlevo jednu pozici pro prvek this, a využijí tu uvolněnou pozici kterou prvek this opouští):
                        column._ColumnOrder = c + 1;
                    else
                        // Prvky, které leží za původní pozicí, budou mít pořadí odpovídající původní pozici v seznamu:
                        column._ColumnOrder = c;
                }
                // poznámka:
                // Jistě, že by šel algoritmus výrazně zkrátit vhodným zkombinováním podmínek.
                // Ale rád bych viděl toho, kdo by se v něm pak vyznal :-)
            }
        }
        /// <summary>
        /// Viditelnost sloupce
        /// </summary>
		public bool Visible 
		{
			get { return this._Visible; }
			set
			{
				if (this._Visible != value)
				{
					this._Visible = value;
					this._OnVisibleChanged();
				}
			}
		}
        /// <summary>
        /// Dostupnost sloupce (Enabled / Disabled)
        /// </summary>
        public bool Enabled
		{
            get { return this._Enabled; }
			set
			{
                if (this._Enabled != value)
				{
                    this._Enabled = value;
					this._OnVisibleChanged();
				}
			}
		}
        /// <summary>
        /// Tento sloupec se má automaticky přizpůsobovat šířce Parenta (tj. řada záhlaví sloupců) tak, aby Parent byl celý naplněn sloupci.
        /// Hodnotu true lze zadat pouze do jednoho sloupce. Zadání true do jednoho sloupce způsobí, že v ostatních sloupcích bude hodnota false.
        /// Sloupec stále akceptuje hodnoty MinimumWidth a MaximumWidth.
        /// </summary>
        public bool ResizeWidthToFillParent
		{
            get { return this._ResizeWidthToFillParent; }
			set
			{
                if (this._ResizeWidthToFillParent != value)
				{
                    // Upozornění: metodu this.Parent.ResetResizeWidthToFillParentExcept() lze volat POUZE při vstupní hodnotě true !!!
                    // Ona totiž metoda this.Parent.ResetResizeWidthToFillParentExcept() do všech sloupců vkládá do property ResizeWidthToFillParent hodnotu false.
                    // A pokud bych tu metodu volal i pro hodnotu false, tak se zacyklím a už se odsud nedostanu...
                    if (value)
                        this.Parent.ResetResizeWidthToFillParentExcept();
                    this._ResizeWidthToFillParent = value;
                    this.Parent.RecalculateColumnWidthToFill();
				}
			}
		}
        /// <summary>
        /// Šířka tohoto sloupce v průběhu interaktivní změny.
        /// Změna hodnoty se projeví voláním eventu Changing, a ne Changed.
        /// Tato šířka se zobrazuje (tu vrací property this.VisibleWidth).
        /// Lze ji omezit hodnotami MinimumWidth a MaximumWidth.
        /// </summary>
        public int WidthInteractive
        {
            get { return this._WidthInteractive; }
            set
            {
                int widthOld = this._WidthInteractive;
                int widthNew = this._WidthIntoRange(value);
                if (widthNew > 0 && widthNew != widthOld)
                {
                    this._WidthInteractive = widthNew;
                    this._OnWidthChanging();
                }
            }
        }
        /// <summary>
        /// Šířka tohoto sloupce.
        /// Změna hodnoty se projeví voláním eventu Changed, a ne Changing.
        /// Lze ji omezit hodnotami MinimumWidth a MaximumWidth.
        /// </summary>
		public int Width
		{
			get { return this._Width; }
			set
			{
                int widthOld = this._Width;
                int widthNew = this._WidthIntoRange(value);
                if (widthNew > 0 && widthNew != widthOld)
				{
                    this._Width = widthNew;
                    this._WidthInteractive = widthNew;                 // Trvalou Width si uložím i do interaktivní!
  					this._OnWidthChanged();
				}
			}
		}
		/// <summary>
		/// Šířka slouupce. Setování nevyvolává žádný event, setuje obě hodnoty (_WidthInteractive i _Width). Get vrací WidthInteractive.
		/// </summary>
		internal int WidthInternal
		{
			get { return this.WidthInteractive; }
			set
			{
				int width = _WidthIntoRange(value);
				this._WidthInteractive = width;
				this._Width = width;
			}
		}
        /// <summary>
        /// Nejmenší povolená šířka
        /// </summary>
        public int? MinimumWidth
        {
            get { return this._MinimumWidth; }
            set
            {
                if (this._MinimumWidth != value)
                {
                    this._MinimumWidth = value;
                    this._WidthCheckRange(true);
                }
            }
        }
        /// <summary>
        /// Největší povolená šířka
        /// </summary>
        public int? MaximumWidth
        {
            get { return this._MaximumWidth; }
            set
            {
                if (this._MaximumWidth != value)
                {
                    this._MaximumWidth = value;
                    this._WidthCheckRange(true);
                }
            }
        }
        /// <summary>
        /// Zajistí zarovnání hodnoty _Width do mezí (_MinimumWidth ÷ _MaximumWidth).
        /// Pokud dojde ke změně, může zavolat event _OnWidthChanged() - podle hodnoty parametru callChange.
        /// Pokud jsou zadány nekorektní hodnoty (_MinimumWidth ÷ _MaximumWidth), nekontroluje nic.
        /// </summary>
        /// <param name="callChange"></param>
        private void _WidthCheckRange(bool callChange)
        {
            // Nekorektní zadání podmínek:
            if (this._MinimumWidth.HasValue && this._MaximumWidth.HasValue && this._MinimumWidth.Value > this._MaximumWidth.Value)
                return;

            // Ošetření šířky:
            int width = this._Width;
            int correct = _WidthIntoRange(width);
            if (correct != width)
            {
                // Uložit korektní hodnotu:
                this._Width = correct;
                
                // Vyvolání eventu:
                if (callChange)
                    this._OnWidthChanged();
            }
        }
        /// <summary>
        /// Vrátí danou hodnotu zarovnanou do mezí Minimum - Maximum
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        private int _WidthIntoRange(int value)
        {
            // Nekorektní zadání podmínek:
            if (this._MinimumWidth.HasValue && this._MaximumWidth.HasValue && this._MinimumWidth.Value > this._MaximumWidth.Value)
                return value;

            if (this._MinimumWidth.HasValue && value < this._MinimumWidth.Value)
                return this._MinimumWidth.Value;
            if (this._MaximumWidth.HasValue && value > this._MaximumWidth.Value)
                return this._MaximumWidth.Value;

            return value;
        }
        /// <summary>
        /// Lze tomuto sloupci měnit šířku?
        /// </summary>
        public bool IsResizable
        {
            get { return this._IsResizable; }
            set { this._IsResizable = value; }
        }
        /// <summary>
        /// Lze tento sloupec přemístit na jiné pořadí?
        /// </summary>
        public bool IsMovable
        {
            get { return this._IsMovable; }
            set { this._IsMovable = value; }
        }
        /// <summary>
        /// Lze tímto sloupcem třídit? Pak může reagovat na Click a nastavovat this.SortOrder.
        /// </summary>
        public bool IsSortEnabled
        {
            get { return this._IsSortEnabled; }
            set { this._IsSortEnabled = value; }
        }
        /// <summary>
        /// Aktuální stav třídění podle tohoto sloupce.
        /// Po změně je vyvolán event RowFilterChanged. Třídění je uvedeno v objektu RowFilter.
        /// </summary>
        public ColumnSortMode SortMode
        {
            get { return this._SortMode; }
            set { this._SortMode = value; }
        }
        /// <summary>
        /// Zarovnání obsahu
        /// </summary>
        public ContentAlignment Alignment
        {
            get { return _Alignment; }
            set
            {
                if (this._Alignment != value)
                {
                    this._Alignment = value;
                    this._OnTextChanged();
                }
            }
        }
        /// <summary>
        /// Tag, který ukládá libovolná data.
        /// </summary>
		public object Tag { get; set; }
        /// <summary>
        /// Viditelná šířka. Pokud je sloupec Invisible, pak je 0. Jinak vrací Width.
        /// Vrací WidthInteractive, protože odráží aktuální stav resize. (V property Width je šířka platná před začátkem resize).
        /// </summary>
		public int VisibleWidth { get { return (this.Visible ? this.WidthInteractive : 0); } }
        /// <summary>
        /// Souřadnice Left, na které tento column začíná. Vždy je dopočítán ze všech předchozích columnů.
        /// </summary>
		public int Left { get { return this.Parent.GetLeft(this); } }
        /// <summary>
        /// Souřadnice Right, na které tento column končí. Vždy je dopočítán ze všech předchozích columnů + this.VisibleWidth.
        /// </summary>
		public int Right { get { return this.Parent.GetLeft(this) + this.VisibleWidth; } }
        /// <summary>
        /// Okraje uvnitř prostoru Columnu
        /// </summary>
        public Padding InnerPadding
        {
            get { return this._InnerPadding; }
            set { this._InnerPadding = value; }
        }
        /// <summary>
        /// Prostor tohoto HeaderColumnu celý, včetně levého a pravého borderu, včetně RowFilter a dolní linky
        /// </summary>
        public Rectangle Bounds { get; private set; }
        /// <summary>
        /// Prostor řádkového filtru v tomto HeaderColumnu. Není Empty, má nenulovou šířku (pokud má samotný sloupec šířku větší než 6).
        /// Může mít nulovou výšku, když řádkový filtr není zobrazen.
        /// </summary>
        public Rectangle RowFilterBounds { get; private set; }
        /// <summary>
        /// Prostor obsahu tohoto Columnu (text záhlaví, časová osa). Okolo tohoto prostoru jsou malé okraje. Pod ním je místo pro řádkový filtr.
        /// </summary>
        public Rectangle HeaderBounds { get; private set; }
		#endregion
		#region HANDLERY INTERNÍCH UDÁLOSTÍ
		private void _OnTextChanged()
		{
			if (this.Parent.SkipEvents) return;
			this.Parent.OnColumnTextChanged(this);
			this.OnTextChanged();
		}
		protected virtual void OnTextChanged()
		{ }
        private void _OnVisibleChanged()
		{
			if (this.Parent.SkipEvents) return;
			this.Parent.OnColumnVisibleChanged(this);
			this.OnVisibleChanged();
		}
		protected virtual void OnVisibleChanged()
		{ }
		private void _OnWidthChanging()
		{
			if (this.Parent.SkipEvents) return;
            this.Parent.OnColumnWidthChanging(this);
            this.OnWidthChanging();
		}
		protected virtual void OnWidthChanging()
		{ }
        private void _OnWidthChanged()
        {
            if (this.Parent.SkipEvents) return;
            this.Parent.OnColumnWidthChanged(this);
            this.OnWidthChanged();
        }
        protected virtual void OnWidthChanged()
        { }
        private void _OnOrderChanged()
        {
            if (this.Parent.SkipEvents) return;
            this.Parent.OnColumnOrderChanged(this);
            this.OnColumnOrderChanged();
        }
        protected virtual void OnColumnOrderChanged()
        { }
        private void _OnHeaderModeChanged()
        {
            this.Parent.CheckPrepareTimeAxis(this);
            if (this.Parent.SkipEvents) return;
            this.Parent.OnColumnHeaderModeChanged(this);
            this.OnHeaderModeChanged();
        }
        protected virtual void OnHeaderModeChanged()
        { }
        private void _OnRowFilterChanged()
        {
            if (this.Parent.SkipEvents) return;
            this.Parent.OnColumnRowFilterChanged(this);
            this.OnRowFilterChanged();
        }
        protected virtual void OnRowFilterChanged()
        { }
        private void _OnSortChanged()
        {
            if (this.Parent.SkipEvents) return;
            this.Parent.OnColumnSortChanged(this);
            this.OnSortChanged();
        }
        protected virtual void OnSortChanged()
        { }
		#endregion
        #region MOUSE
        /// <summary>
        /// Obsahuje true, pokud je tento column pod myší (při pohybu myši, při zmáčknutí myší)
        /// </summary>
        public bool IsHot
        { get { return this.Parent.IsColumnHot(this); } }
        /// <summary>
        /// Obsahuje true, pokud je tento column právě zmáčknut levým tlačítkem myši
        /// </summary>
        public bool IsMouseDownLeft
        { get { return this.Parent.IsColumnMouseDownLeft(this); } }
        /// <summary>
        /// Obsahuje true, pokud je tento column právě zmáčknut levým tlačítkem myši
        /// </summary>
        public bool IsMouseDownRight
        { get { return this.Parent.IsColumnMouseDownRight(this); } }
        /// <summary>
        /// Vrátí true, pokud se tento prvek nachází na dané souřadnici (v koordinátech svého parenta = DColumns)
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        public bool IsOnPoint(Point point)
        {
            if (this.VisibleWidth <= 0) return false;
            if (this.Bounds.IsEmpty) return false;
            return (this.Bounds.Contains(point));
        }
		#endregion
		#region PAINT, CALCULATE BOUNDS
		/// <summary>
		/// Nakreslí se
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		internal void Paint(object sender, PaintEventArgs e)
		{
            // Určit rozměry Bounds v tomto Columnu
            this.CalculateBounds();

            bool focused = false;
            System.Windows.Forms.VisualStyles.TabItemState state = _GetState();

            // Umístit objekt řádkového filtru do prostoru this.RowFilterBounds:
            this._RowFilterSetPosition();

            // Vykreslit grafiku záhlaví, včetně textu, podle typu:
            switch (this.HeaderMode)
            {
                case DColumnHeaderMode.StandardHeader:
                    _PaintStandardHeader(e.Graphics, state, focused);
                    break;
                case DColumnHeaderMode.TimeAxis:
                    _PaintTimeAxis(e.Graphics, state, focused);
                    break;
            }
		}
        /// <summary>
        /// Metoda určí všechny Bounds tohoto sloupce.
        /// </summary>
        internal void CalculateBounds()
        {
            this.Bounds = this._GetBounds();
            this.RowFilterBounds = this._GetRowFilterBounds();
            this.HeaderBounds = this._GetHeaderBounds();
        }
        /// <summary>
        /// Vykreslí standardní záhlaví sloupce
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="state"></param>
        /// <param name="focused"></param>
        /// <param name="rfBound"></param>
        private void _PaintStandardHeader(Graphics graphics, System.Windows.Forms.VisualStyles.TabItemState state, bool focused)
        {
            // Vykreslit pozadí a okraj:
            _PaintBackHeader(graphics, state, focused);

            // Vypsat text do prostoru, který zbývá nad řádkovým filtrem:
            Rectangle bound = this.HeaderBounds;
			string text = this.Text;
            bool isSorting = (this.SortMode == ColumnSortMode.Ascending || this.SortMode == ColumnSortMode.Descending);
            FontStyle fontStyle = (isSorting ? FontStyle.Bold : FontStyle.Regular);
            TextFormatFlags tff = TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.LeftAndRightPadding; // | TextFormatFlags.SingleLine;
            using (Font font = new Font(this.Parent.Font, fontStyle))
            {
                TextRenderer.DrawText(graphics, text, font, bound, Color.Black, tff);

                // Vykreslit ikonu třídění sloupce ASC/DESC před text:
                if (isSorting && bound.Width > 20)
                {
                    StringFormat stf = new StringFormat(StringFormatFlags.NoWrap);
                    SizeF textS = graphics.MeasureString(text, font, new SizeF(bound.Width, bound.Height), stf);
                    Image icon = (this.SortMode == ColumnSortMode.Ascending ? PicLibrary16.GoUp_Cached : PicLibrary16.GoDn_Cached);
                    Size iconS = icon.Size;                                       // Velikost ikonky
                    int x = ((bound.Width - (int)textS.Width) / 2) - iconS.Width; // Počet pixelů ležících mezi bound.X a skutečným začátkem textu, kam je vyrenderován při horizontálním zarovnání na střed, snížený šířku ikony Up/Down
                    if (x < 0) x = 0;
                    x += bound.X;
                    int y = bound.Y + (bound.Height - iconS.Height) / 2;          // Souřadnice Top pro ikonu: ikona bude vertikálně ležet uprostřed prostoru bound.Height
                    Rectangle iconB = new Rectangle(x, y, iconS.Width, iconS.Height);
                    graphics.DrawImage(icon, iconB);
                }
            }
        }
        /// <summary>
        /// Vykreslí záhlaví s časovou osou
        /// </summary>
        private void _PaintTimeAxis(Graphics graphics, System.Windows.Forms.VisualStyles.TabItemState state, bool focused)
        {
            // Vykreslit pozadí a okraj:
            _PaintBackHeader(graphics, state, focused);

            // Umístit časovou osu:
            this.Parent.TimeAxisSetBounds(this.HeaderBounds, true);
        }
        /// <summary>
        /// Vykreslí pozadí záhlaví (linku okraje + barvu pozadí), nekreslí text a ani ikonu třídění
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="state"></param>
        /// <param name="focused"></param>
        /// <param name="rfBound"></param>
        private void _PaintBackHeader(Graphics graphics, System.Windows.Forms.VisualStyles.TabItemState state, bool focused)
        {
            if (TabRenderer.IsSupported)
            {
                // Upravím Bounds tak, aby na sebe těsněji navazovaly:
                Rectangle bounds = this.Bounds;
                if (bounds.X > 0)
                {
                    bounds.X = bounds.X - 1;
                    bounds.Width = bounds.Width + 2;
                }
                else
                {
                    bounds.Width = bounds.Width + 1;
                }
                TabRenderer.DrawTabItem(graphics, bounds, focused, state);
            }
            else
            {
                this._PaintTabManual(graphics, focused, state);
            }
        }
        /// <summary>
        /// Ruční kreslení TABu, v případě když TabRenderer.IsSupported je false
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="bound"></param>
        /// <param name="state"></param>
        private void _PaintTabManual(Graphics graphics, bool focused, System.Windows.Forms.VisualStyles.TabItemState state)
        {
            Rectangle bound = this.Bounds;
            Color color1 = Color.FromArgb(231, 236, 226);
            Color color2 = DblGraphControl.ColorShift(color1, -48, -48, -32);

            // Barva, odpovídající stavu:
            switch (state)
            {
                case System.Windows.Forms.VisualStyles.TabItemState.Normal:
                    break;
                case System.Windows.Forms.VisualStyles.TabItemState.Hot:
                    color1 = Color.FromArgb(228, 244, 255);
                    color2 = DblGraphControl.ColorShift(color1, -48, -48, -32);
                    break;
                case System.Windows.Forms.VisualStyles.TabItemState.Selected:
                    color1 = Color.FromArgb(197, 228, 236);
                    color2 = DblGraphControl.ColorShift(color1, -32, -32, -32);
                    break;
                case System.Windows.Forms.VisualStyles.TabItemState.Disabled:
                    color1 = SystemColors.ButtonShadow;
                    color2 = DblGraphControl.ColorShift(color1, -16, -16, -16);
                    break;
            }
            Point[] points;

            // Pozadí:
            points = _PaintTabManualGetPoint(bound, true);
            using (LinearGradientBrush lgb = new LinearGradientBrush(bound, color1, color2, 90F))
            {
                // lgb.SetSigmaBellShape(0.40F, 0.80F);
                lgb.SetBlendTriangularShape(0.25F, 0.75F);
                graphics.FillPolygon(lgb, points);
            }

            // Okraj:
            points = _PaintTabManualGetPoint(bound, false);
            using (Pen pen = new Pen(Color.Black))
            {
                graphics.DrawPolygon(pen, points);
            }
        }
        /// <summary>
        /// Určí a vrátí stav headeru
        /// </summary>
        /// <returns></returns>
        private System.Windows.Forms.VisualStyles.TabItemState _GetState()
        {
            if (!this.Enabled)
                return System.Windows.Forms.VisualStyles.TabItemState.Disabled;
            if (this.IsMouseDownLeft)
                return System.Windows.Forms.VisualStyles.TabItemState.Selected;
            if (this.IsHot)
                return System.Windows.Forms.VisualStyles.TabItemState.Hot;

            return System.Windows.Forms.VisualStyles.TabItemState.Normal;
        }
        /// <summary>
        /// Vrátí pole bodů, které tvoří okraj ColumnHeaderu při ručním kreslení
        /// </summary>
        /// <param name="bound"></param>
        /// <param name="closed"></param>
        /// <returns></returns>
        private Point[] _PaintTabManualGetPoint(Rectangle bound, bool closed)
        {
            List<Point> points = new List<Point>();
            points.Add(new Point(bound.X, bound.Bottom));
            points.Add(new Point(bound.X, bound.Y + 1));
            points.Add(new Point(bound.X + 1, bound.Y));
            points.Add(new Point(bound.Right - 1, bound.Y));
            points.Add(new Point(bound.Right, bound.Y + 1));
            points.Add(new Point(bound.Right, bound.Bottom));

            if (closed)
                points.Add(points[0]);
            return points.ToArray();
        }
        /// <summary>
        /// Vypočítá a vrátí Bounds pro aktuální ColumnHeader, plná velikost včetně dolního řádkového filtru.
        /// </summary>
        /// <returns></returns>
        private Rectangle _GetBounds()
        {
            int width = this.VisibleWidth;
            if (width <= 0) return Rectangle.Empty;

            int left = this.Left;
            return new Rectangle(left, 0, width, this.Parent.Height);
        }
        /// <summary>
        /// Vypočítá a vrátí RowFilterBounds. Počítá se až po výpočtu Bounds.
        /// </summary>
        /// <returns></returns>
        private Rectangle _GetRowFilterBounds()
        {
            Rectangle bounds = this.Bounds;
            if (bounds.IsEmpty) return Rectangle.Empty;

            int w = bounds.Width - this._InnerPadding.Horizontal;
            if (w <= 0) return Rectangle.Empty;

            int h = 0;
            if (this.IsSystemColumn)
            {   // Pro systémový column určím výšku řádkového filtru jinak: tento column totiž neobsahuje DRowFilter, ale button ClearFilter, a ten má fixní výšku 16:
                h = 16;
            }
            else if (this._FilterItem != null && this._FilterItem.Visible)
            {   // Pro běžný column :
                h = this._FilterItem.Height;
                if (h < 10)
                {
                    h = this._FilterItem.HeightOptimal;
                    this._FilterItem.Height = h;
                }
            }

            return new Rectangle((bounds.X + this._InnerPadding.Left), (this.Parent.Height - this._InnerPadding.Vertical - h - 1), w, h);
        }
        /// <summary>
        /// Vypočítá a vrátí HeaderBounds. Počítá se až jako poslední (nejprve Bounds, pak RowFilterBounds, a nakonec HeaderBounds).
        /// </summary>
        /// <returns></returns>
        private Rectangle _GetHeaderBounds()
        {
            Rectangle bounds = this.Bounds;
            if (bounds.IsEmpty) return Rectangle.Empty;

            Rectangle rowFilterBounds = this.RowFilterBounds;
            if (rowFilterBounds.Width <= 0) return Rectangle.Empty;           // Málo místa na šířku.

            int y = bounds.Y + this._InnerPadding.Top;                        // Top
            int b = rowFilterBounds.Y - 1;                                    // Bottom
            return new Rectangle(rowFilterBounds.X, y, rowFilterBounds.Width, b - y);
        }
		#endregion
        #region ROW FILTER, SORT
        /// <summary>
        /// Zajistí korektní umístění našeho RowFilter
        /// </summary>
        internal void RowFilterReBounds()
        {
            if (this._RowFilterVisible || this.IsSystemColumn)
            {
                this.CalculateBounds();
                this._RowFilterSetPosition();
            }
        }
        /// <summary>
        /// Nastavení hodnoty this._RowFilterVisible
        /// </summary>
        /// <param name="value"></param>
        private void _RowFilterSetVisible(bool value)
        {
            if (this.IsSystemColumn) return;              // Na systémový column tohleto neplatí. Jeho Visible se řídí jinak...

            this._RowFilterVisible = value;
            if (!this._RowFilterVisible)
            {   // Pokud nemá být Visible, tak prvek schovám a skončím:
                if (this._FilterItem != null)
                    this._FilterItem.Visible = false;
                return;
            }
            if (this._FilterItem == null)
                // Protože má být Visible, tak pokud neexistuje pak jej vytvořím:
                this._RowFilterCreate();

            // Umístit prvek podle pozice this (=Column):

            // Zajistit jeho Visible:
            if (!this._FilterItem.Visible)
                this._FilterItem.Visible = true;
        }
        /// <summary>
        /// Vytvoří vnitřní prvek DRowFilter _DRowFilter a (protože je to Control) tak jej přidá do this.Parent.Controls
        /// </summary>
        private void _RowFilterCreate()
        {
            this._FilterItem = new DRowFilterItem(this);
            this._FilterItem.FilterChanged += new EventHandler(_RowFilter_FilterChanged);
            this._FilterItem.FilterChangeFocus += new EventHandler(_RowFilter_FilterChangeFocus);
            this._FilterItem.MouseEnter += new EventHandler(_RowFilter_MouseEnter);
            this._FilterItem.MouseLeave += new EventHandler(_RowFilter_MouseLeave);
            this._FilterItem.Visible = false;
            this.Parent.Controls.Add(this._FilterItem);
        }
        /// <summary>
        /// Vytvoří control _FilterClear, pro systémový column.
        /// </summary>
        private void _FilterClearCreate()
        {
            this._FilterClear = new DRowFilterClear();
            this._FilterClear.FilterClear += new EventHandler(_FilterClear_Click);
            this._FilterClear.MouseEnter += new EventHandler(_RowFilter_MouseEnter);
            this._FilterClear.MouseLeave += new EventHandler(_RowFilter_MouseLeave);
            this._FilterClear.Visible = false;
            this.Parent.Controls.Add(this._FilterClear);
        }
        /// <summary>
        /// Uživatel kliknul na button FilterClear.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        private void _FilterClear_Click(object sender, EventArgs args)
        {
            this.Parent.RowFilterClear();
        }
        /// <summary>
        /// Nuluje řádkový filtr pro tuto jednu položku (nastaví ji do výchozího stavu).
        /// Volitelně je možno potlačit event FilterChanged.
        /// </summary>
        /// <param name="suppresFilterChangedEvent">true = potlačit event volaný při změně filtru</param>
        internal void RowFilterClear(bool suppresFilterChangedEvent)
        {
            if (this._FilterItem != null)
                this._FilterItem.Clear(suppresFilterChangedEvent);
        }
        /// <summary>
        /// Příznak, že se myš pohybuje nad objektem this._RowFilter, a že tedy this column má být kreslen jako Hot
        /// </summary>
        internal bool RowFilterHasMouse { get; set; }
        void _RowFilter_MouseEnter(object sender, EventArgs e)
        {
			// string snd = (sender is Control ? sender.GetType().Name + ": " + ((Control)sender).Name : sender.ToString());
			// Steward.AuditInfo("MouseEnter: " + snd);
            this.RowFilterHasMouse = true;
            this.Parent.SetMouseStatus(DColumns.MouseMode.LeaveOnFilter, this, null);
        }
        void _RowFilter_MouseLeave(object sender, EventArgs e)
        {
			// string snd = (sender is Control ? sender.GetType().Name + ": " + ((Control)sender).Name : sender.ToString());
			// Steward.AuditInfo("MouseLeave: " + snd);
            this.RowFilterHasMouse = false;
            this.Parent.SetMouseStatus(DColumns.MouseMode.None, null, null);
        }
        /// <summary>
        /// Změnila se hodnota filtru v tomto sloupci
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _RowFilter_FilterChanged(object sender, EventArgs e)
        {
            this._OnRowFilterChanged();
        }
        /// <summary>
        /// Filtr dostal nebo ztratil focus (do vstupního políčka)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _RowFilter_FilterChangeFocus(object sender, EventArgs e)
        {
            this.Parent.Draw();                // Překreslist ColumnHeader, budou jiné barvy Hot sloupce (nyní je to ten s focusem)
        }
        /// <summary>
        /// Nastaví pro zdejší RowFilter jeho Visible a Bounds.
        /// Umístit objekt řádkového filtru do prostoru this.RowFilterBounds.
        /// </summary>
        private void _RowFilterSetPosition()
        {
            if (this.IsSystemColumn)
                _RowFilterSetPositionSystem();
            else
                _RowFilterSetPositionStandard();
        }
        /// <summary>
        /// Umístí button FilterClear do prostoru this.RowFilterBounds a zajistí jeho viditelnost
        /// </summary>
        private void _RowFilterSetPositionSystem()
        {
            if (this._FilterClear == null) return;

            Rectangle rowFilterBounds = this.RowFilterBounds;
            if (rowFilterBounds.IsEmpty) return;

            // Visible:
            bool rfVisible = this.Parent.ContainRowFilterInVisibleColumns;         // Parent mi zjistí, zda některý viditelný sloupec (kromě systémových) zobrazuje řádkový filtr.
            if (rfVisible && !this._FilterClear.Visible)
                this._FilterClear.Visible = true;
            else if (!rfVisible && this._FilterClear.Visible)
                this._FilterClear.Visible = false;

            // Bounds:
            if (rfVisible && this._FilterClear.Bounds != rowFilterBounds)
                this._FilterClear.Bounds = rowFilterBounds;
        }
        /// <summary>
        /// Umístí řádkový filtr do prostoru this.RowFilterBounds a zajistí jeho viditelnost
        /// </summary>
        private void _RowFilterSetPositionStandard()
        {
            if (this._FilterItem == null) return;

            Rectangle rowFilterBounds = this.RowFilterBounds;
            if (rowFilterBounds.IsEmpty) return;

            // Visible:
            bool rfVisible = (rowFilterBounds.Width > 0 && this.RowFilterVisible);
            if (rfVisible && !this._FilterItem.Visible)
                this._FilterItem.Visible = true;
            else if (!rfVisible && this._FilterItem.Visible)
                this._FilterItem.Visible = false;

            // Bounds:
            if (rfVisible && this._FilterItem.Bounds != rowFilterBounds)
				this._FilterItem.Bounds = rowFilterBounds;
        }
        /// <summary>
        /// Metoda vrátí režim třídění, který bude platit po kliknutí na sloupec.
        /// Pokud tento sloupec neumožňuje třídit, bude vracet None.
        /// Pokud tento sloupec umožňuje třídění, bude vracet hodnotu v cyklu None - Ascending - Descending - None..., vycházející ze současného stavu.
        /// </summary>
        /// <returns></returns>
        internal ColumnSortMode GetNextSortMode()
        {
            if (!this.IsSortEnabled) return ColumnSortMode.None;
            if (this.SortMode == ColumnSortMode.None) return ColumnSortMode.Ascending;
            if (this.SortMode == ColumnSortMode.Ascending) return ColumnSortMode.Descending;
            return ColumnSortMode.None;
        }
        #endregion
    }
    /// <summary>
    /// Režim zobrazení sloupce
    /// </summary>
    public enum DColumnHeaderMode
    {
        None = 1,
        /// <summary>
        /// Standardní záhlaví
        /// </summary>
        StandardHeader,
        /// <summary>
        /// Časová osa.
        /// Tento režim smí být použit nanejvýše u jednoho sloupce.
        /// Objekt časové osy je dostupný v property DColumns.TimeAxis.
        /// Pod časovou osou může/nemusí být zobrazen řádkový filtr, záleží na property DColumn.RowFilterVisible
        /// </summary>
        TimeAxis
    }
    #endregion
	#region CLASS DColumnsSynchronizer : třída pro synchronizaci dat více instancí třídy DColumns
	/// <summary>
	/// DColumnsSynchronizer : třída pro synchronizaci dat více instancí třídy DColumns.
    /// Synchronizer zajišťuje, že mezi jednotlivými instancemi třídy DColumns se navzájem přenášejí informace o předem vybraných vlastnostech
    /// (například šířka, viditelnost a ColumnOrder sloupců), a do nadřízené vrstvy se pak předává event jen jeden (globální) namísto eventů od každého objektu.
    /// Vlastnosti, které se nesynchronizují, se do nadřízené vrstvy předávají jednotlivě.
	/// </summary>
	public class DColumnsSynchronizer
	{
		#region KONSTRUKCE
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="synchronizedItems">Vlastnosti, které bude synchronizer udržovat synchronní</param>
        /// <param name="sendSynchronizedEvents">Vyvolávat eventy (nahoru) za každý jednotlivý graf? 
        /// false = default = za každou událost poslat jen jeden event od fyzicky aktivního grafu, ale od slave grafů se event posílat nebude.
        /// true = není implementováno.</param>
        public DColumnsSynchronizer(DColumnsSynchronizedItems synchronizedItems, bool sendSynchronizedEvents)
		{
            this.SyncObjList = new List<IHeaderSync>();
            this._SynchronizedItems = synchronizedItems;
            this._SendSynchronizedEvents = sendSynchronizedEvents;
		}
        private List<IHeaderSync> SyncObjList;
        private DColumnsSynchronizedItems _SynchronizedItems;
        private bool _SendSynchronizedEvents;
		#endregion
		#region PUBLIC METODY PRO ZALISTOVÁNÍ A ODLISTOVÁNÍ
        /// <summary>
        /// Přehled synchronizovaných vlastností.
        /// Lze jej změnit, ale změna nezajistí okamžitou synchronizaci podle nového nastavení. Promítne se až při další změně.
        /// </summary>
        /// <remarks>
        /// Mohl bych napsat metodu SynchronizePropertyAccordingTo(DColumns original), ale zatím toho není zapotřebí.
        /// </remarks>
        public DColumnsSynchronizedItems SynchronizedItems
        {
            get { return _SynchronizedItems; }
            set { _SynchronizedItems = value; }
        }
        /// <summary>
        /// Posílat i eventy od synchronizovaných grafů?
        /// false = ne, event přijde jen od grafu, který je zdrojem události.
        /// true = ano, event přijde od všech grafů, i těch které se synchronizují.
        /// </summary>
        public bool SendSynchronizedEvents
        {
            get { return _SendSynchronizedEvents; }
            set { _SendSynchronizedEvents = value; }
        }
		/// <summary>
		/// Do synchronizeru přidá další sadu sloupců
		/// </summary>
		/// <param name="iSyncObj"></param>
        public void Enlist(IHeaderSync iSyncObj)
		{
			if (iSyncObj == null)
				Throw.SysError(MessageInfo.Get("DColumnsSynchronizer: Nelze zalistovat IHeaderSync, který je null"));
			if (this.SyncObjList.Exists(c => Object.ReferenceEquals(iSyncObj, c)))
				Throw.SysError(MessageInfo.Get("DColumnsSynchronizer: Nelze zalistovat IHeaderSync, který je již zalistovaný"));

			this._LinkEvents(iSyncObj);

			this.SyncObjList.Add(iSyncObj);
		}
		/// <summary>
		/// Ze synchronizeru odebere danou sadu sloupců
		/// </summary>
		/// <param name="iSyncObj"></param>
        public void Unlist(IHeaderSync iSyncObj)
		{
			if (iSyncObj == null)
				Throw.SysError(MessageInfo.Get("DColumnsSynchronizer: Nelze odlistovat IHeaderSync, který je null"));
			
			this._UnLinkEvents(iSyncObj);
			
			this.SyncObjList.RemoveAll(c => Object.ReferenceEquals(iSyncObj, c));
		}
		/// <summary>
		/// Zaregistruje zdejší eventhandlery na události daného Columns
		/// </summary>
		/// <param name="iSyncObj"></param>
		private void _LinkEvents(IHeaderSync iSyncObj)
		{
			iSyncObj.ColumnHeaderHeightChanging += new EventHandler(_SyncObj_ColumnHeaderHeightChanging);
			iSyncObj.ColumnHeaderHeightChanged += new EventHandler(_SyncObj_ColumnHeaderHeightChanged);
            iSyncObj.ColumnWidthChanging += new DColumnWidthEventHandler(_SyncObj_ColumnWidthChanging);
            iSyncObj.ColumnWidthChanged += new DColumnWidthEventHandler(_SyncObj_ColumnWidthChanged);
			iSyncObj.ColumnVisibleChanged += new DColumnEventHandler(_SyncObj_ColumnVisibleChanged);
			iSyncObj.ColumnSortChanged += new DColumnEventHandler(_SyncObj_ColumnSortChanged);
			iSyncObj.RowFilterChanged += new DColumnRowFilterEventHandler(_SyncObj_RowFilterChanged);
			iSyncObj.TimeAxisChange += new TimeAxisChangeHandler(_SyncObj_TimeAxisChange);
		}
		/// <summary>
		/// Odregistruje zdejší eventhandlery
		/// </summary>
		/// <param name="iSyncObj"></param>
        private void _UnLinkEvents(IHeaderSync iSyncObj)
		{
			iSyncObj.ColumnHeaderHeightChanging -= new EventHandler(_SyncObj_ColumnHeaderHeightChanging);
			iSyncObj.ColumnHeaderHeightChanged -= new EventHandler(_SyncObj_ColumnHeaderHeightChanged);
            iSyncObj.ColumnWidthChanging -= new DColumnWidthEventHandler(_SyncObj_ColumnWidthChanging);
            iSyncObj.ColumnWidthChanged -= new DColumnWidthEventHandler(_SyncObj_ColumnWidthChanged);
			iSyncObj.ColumnVisibleChanged -= new DColumnEventHandler(_SyncObj_ColumnVisibleChanged);
			iSyncObj.ColumnSortChanged -= new DColumnEventHandler(_SyncObj_ColumnSortChanged);
			iSyncObj.RowFilterChanged -= new DColumnRowFilterEventHandler(_SyncObj_RowFilterChanged);
			iSyncObj.TimeAxisChange -= new TimeAxisChangeHandler(_SyncObj_TimeAxisChange);
		}
		/// <summary>
		/// Obsahuje true, když v tomto objektu máme referenci na alespoň jeden objekt IHeaderSync
		/// </summary>
		private bool _SyncObjExists
		{ get { return (_SyncObjFirst != null); } }
		/// <summary>
		/// Obsahuje referenci na první existující IHeaderSync, anebo null.
		/// </summary>
        private IHeaderSync _SyncObjFirst
		{ get { return this.SyncObjList.Find(c => c != null); } }
		#endregion
		#region DEKLARACE PUBLIC EVENTŮ - EVENTY, KTERÉ VOLÁ SYNCHRONIZER NAMÍSTO JEDNOTLIVÝCH COLUMNS
		/// <summary>
		/// Event, který bude vyvolán opakovaně v průběhu interaktivní (výhradně !) změny výšky záhlaví
		/// </summary>
		[Browsable(true)]
		[Category("Action")]
		[Description("Event, který bude vyvolán opakovaně v průběhu interaktivní (výhradně !) změny výšky záhlaví")]
		public event EventHandler ColumnHeaderHeightChanging;
		/// <summary>
		/// Event, který bude vyvolán po dokončení interaktivní (výhradně !) změny výšky záhlaví
		/// </summary>
		[Browsable(true)]
		[Category("Action")]
		[Description("Event, který bude vyvolán po dokončení interaktivní (výhradně !) změny výšky záhlaví")]
		public event EventHandler ColumnHeaderHeightChanged;
		/// <summary>
		/// Event, který bude vyvolán opakovaně v průběhu interaktivní (výhradně !) změny šířky konkrétního sloupce
		/// </summary>
		[Browsable(true)]
		[Category("Action")]
		[Description("Event, který bude vyvolán opakovaně v průběhu interaktivní (výhradně !) změny šířky některého sloupce")]
		public event DColumnWidthEventHandler ColumnWidthChanging;
		/// <summary>
		/// Event, který bude vyvolán po dokončení interaktivní (výhradně !) změny šířky konkrétního sloupce
		/// </summary>
		[Browsable(true)]
		[Category("Action")]
		[Description("Event, který bude vyvolán po změně šířky některého sloupce")]
		public event DColumnWidthEventHandler ColumnWidthChanged;
        /// <summary>
        /// Event, který bude vyvolán po změně pořadí sloupce (a přepočtu pořadí ostatních sloupců)
        /// </summary>
        [Browsable(true)]
        [Category("Action")]
        [Description("Event, který bude vyvolán po změně pořadí sloupce (a přepočtu pořadí ostatních sloupců)")]
        public event DColumnEventHandler ColumnOrderChanged;
        /// <summary>
		/// Event, který bude vyvolán po změně hodnoty Visible konkrétního sloupce
		/// </summary>
		[Browsable(true)]
		[Category("Action")]
		[Description("Event, který bude vyvolán po změně hodnoty Visible konkrétního sloupce")]
		public event DColumnEventHandler ColumnVisibleChanged;
		/// <summary>
		/// Event, který bude vyvolán po jakékoli změně v řádkovém filtru
		/// </summary>
		[Browsable(true)]
		[Category("Action")]
		[Description("Event, který bude vyvolán po jakékoli změně v řádkovém filtru")]
		public event DColumnRowFilterEventHandler RowFilterChanged;
		/// <summary>
		/// Event, který bude vyvolán po změně třídění na některém sloupci
		/// </summary>
		[Browsable(true)]
		[Category("Action")]
		[Description("Event, který bude vyvolán po změně třídění na některém sloupci")]
		public event DColumnEventHandler ColumnSortChanged;
		/// <summary>
		/// { get; set } Vizuální rozsah časové osy
		/// </summary>
		[Browsable(true)]
		[Category("Action")]
		[Description("Vizuální rozsah časové osy")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public TimeRange CurrentTimeRange
		{
			get
			{
				if (this._SyncObjExists)
					return this._SyncObjFirst.CurrentTimeRange;
				return TimeRange.Empty;
			}
			set
			{
                bool suppressEvents = this.SuppressEvents;
                try
                {
                    this.SuppressEvents = true;    // Metoda by skrze TimeAxis vyvolávala rekurzivně event _Columns_TimeAxisChange a přes synchronizer by se prováděla do ostatních grafů. Potlačit.
				    this.SyncObjList.ForEach(c => c.CurrentTimeRange = value);
			    }
		        finally
		        {
                    this.SuppressEvents = suppressEvents;
		        }
            }
        }
		/// <summary>
		/// Aktuální seznam ticků osy.
		/// </summary>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Browsable(false)]
		public List<TimeAxisTickInfo> AxisTicks
		{
			get
			{
				if (this._SyncObjExists)
					return this._SyncObjFirst.AxisTicks;
				return null;
			}
		}
		/// <summary>
		/// Reference na živý objekt časového kalkulátoru (aktuální rozsah, měřítko, vizuální přepočty)
		/// </summary>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Browsable(false)]
		public ITimeAxisCalc TimeAxisCalc
		{
			get
			{
				if (this._SyncObjExists)
					return this._SyncObjFirst.TimeAxisCalc;
				return null;
			}
		}
		/// <summary>
		/// Event, který bude vyvolán při změnách zobrazeného času na časové ose
		/// </summary>
		[Browsable(true)]
		[Category("Action")]
		[Description("Event, který bude vyvolán při změnách zobrazeného času na časové ose")]
		public event TimeAxisChangeHandler TimeAxisChange;
		/// <summary>
		/// Nastaví vlastnosti časové osy.
		/// Je voláno zvenku (typicky z Gridu, po DoubleClicku na element, kdy Grid chce zobrazit jeho celou délku).
        /// Nastaví časovou osu, a jedenkrát vyvolá event OnTimeAxisChange.
		/// </summary>
		/// <param name="e">Data pro požadovanou změnu</param>
        public void SetTimeAxisData(TimeAxisSetAxisArgs e)
		{
            bool suppressEvents = this.SuppressEvents;
            try
            {
                this.SuppressEvents = true;    // Metoda by skrze TimeAxis vyvolávala rekurzivně event _Columns_TimeAxisChange a přes synchronizer by se prováděla do ostatních grafů. Potlačit.
                this.SyncObjList.ForEach(c => c.SetTimeAxisData(e));
            }
		    finally
		    {
                this.SuppressEvents = suppressEvents;
		    }
		}
		/// <summary>
		/// Zastaví případný pohyb na časové ose
		/// </summary>
		public void StopTimeAxisMove()
		{
			this.SyncObjList.ForEach(c => c.StopTimeAxisMove());
		}
		#endregion
		#region PUBLIC METODY A PROPERTY PRO DISTRIBUCI METOD
		/// <summary>
		/// Potlačení kreslení Headeru : promítnout i do TimeOriginal.
		/// </summary>
		internal void SuppressDrawingPush()
		{
			foreach (IHeaderSync target in this.SyncObjList)
				target.SuppressDrawingPush();
		}
		/// <summary>
		/// Obnovení kreslení Headeru : promítnout i do TimeOriginal.
		/// </summary>
		internal void SuppressDrawingPop()
		{
			foreach (IHeaderSync target in this.SyncObjList)
				target.SuppressDrawingPop();
		}
		/// <summary>
		/// Potlačení kreslení při provádění rozsáhlejších změn. Po ukončení je třeba nastavit na false !
		/// Default = false = kreslení není potlačeno.
		/// Při provádění rozsáhlejších změn je vhodné nastavit na true, a po dokončení změn vrátit na false => tím se automaticky vyvolá Draw.
		/// </summary>
		internal bool SuppressDrawing
		{
			get
			{
				if (this._SyncObjExists)
					return this._SyncObjFirst.SuppressDrawing;
				return false;
			}
			set
			{
				foreach (IHeaderSync target in this.SyncObjList)
					target.SuppressDrawing = value;
			}
		}
        /// <summary>
        /// Potlačení předávání veškerých eventů jako mezi synchronizovanými objekty, tak do nadřízeného eventu
        /// </summary>
        internal bool SuppressEvents { get; set; }
		#endregion
        #region EVENTHANDLERY PRO UDÁLOSTI V IHeaderSync
        void _SyncObj_ColumnHeaderHeightChanging(object sender, EventArgs args)
		{
            if (this.SuppressEvents) return;
            _Sync_ColumnHeight(sender, args, this.ColumnHeaderHeightChanging, DColumnsSynchronizedItems.ColumnHeaderHeightChanging, true);
		}
        void _SyncObj_ColumnHeaderHeightChanged(object sender, EventArgs args)
		{
            if (this.SuppressEvents) return;
            _Sync_ColumnHeight(sender, args, this.ColumnHeaderHeightChanged, DColumnsSynchronizedItems.ColumnHeaderHeightChanged, false);
		}
        void _SyncObj_ColumnWidthChanging(object sender, DColumnWidthEventArgs args)
		{
            if (this.SuppressEvents) return;
            _Sync_ColumnWidth(sender, args, this.ColumnWidthChanging, DColumnsSynchronizedItems.ColumnWidthChanging, true);
		}
        void _SyncObj_ColumnWidthChanged(object sender, DColumnWidthEventArgs args)
		{
            if (this.SuppressEvents) return;
            _Sync_ColumnWidth(sender, args, this.ColumnWidthChanged, DColumnsSynchronizedItems.ColumnWidthChanged, false);
		}
		void _SyncObj_ColumnVisibleChanged(object sender, DColumnEventArgs args)
		{
            if (this.SuppressEvents) return;
            _Sync_ColumnVisible(sender, args, this.ColumnVisibleChanged, DColumnsSynchronizedItems.ColumnVisibleChanged, false);
		}
        void _Columns_ColumnOrderChanged(object sender, DColumnEventArgs args)
        {
            if (this.SuppressEvents) return;
            _Sync_ColumnOrder(sender, args, this.ColumnOrderChanged, DColumnsSynchronizedItems.ColumnOrderChanged, false);
        }
        void _SyncObj_ColumnSortChanged(object sender, DColumnEventArgs args)
		{
            if (this.SuppressEvents) return;
            _Sync_ColumnSort(sender, args, this.ColumnSortChanged, DColumnsSynchronizedItems.ColumnSortChanged, false);
		}
		void _SyncObj_RowFilterChanged(object sender, DColumnRowFilterEventArgs args)
		{
            if (this.SuppressEvents) return;
            if (this.RowFilterChanged != null)
				this.RowFilterChanged(sender, args);
		}
		void _SyncObj_TimeAxisChange(object sender, TimeAxisChangeEventArgs args)
		{
            if (this.SuppressEvents) return;
            _Sync_TimeAxisRange(sender, args, this.TimeAxisChange, DColumnsSynchronizedItems.TimeAxisRangeChanged, false);
		}
		#endregion
		#region VÝKONNÉ METODY SYNCHRONIZACE
        /// <summary>
        /// Synchronizuje výšku sloupců, po dobu synchronizace potlačuje rekurzivní volání téhož eventu
        /// </summary>
        /// <param name="sender">Odesilatel události, typicky IHeaderSync, kde k události došlo</param>
        /// <param name="args">Data události</param>
        /// <param name="handler">Handler eventu - nahoru</param>
        /// <param name="item">Typ vlastnosti, ten se testuje zda se má/nemá synchronizovat</param>
        /// <param name="interactive">Interaktivní režim</param>
        private void _Sync_ColumnHeight(object sender, EventArgs args, EventHandler handler, DColumnsSynchronizedItems item, bool interactive)
        {
            if (_SyncInProgress) return;
            try
            {
                _SyncInProgress = true;

                if (_IsToSync(item))
                {
                    IHeaderSync source = sender as IHeaderSync;
                    if (source != null)
                    {
                        foreach (IHeaderSync target in this.SyncObjList.FindAll(c => !Object.ReferenceEquals(c, source)))
                            // Pro všechny IHeaderSync, které jsou jiné než source:
                            target.SychronizeColumnHeightFrom(source, args, interactive);
                    }
                }
                if (handler != null)
                    handler(sender, args);
            }
            finally
            {
                _SyncInProgress = false;
            }
        }
		/// <summary>
		/// Synchronizuje šířky sloupců, po dobu synchronizace potlačuje rekurzivní volání téhož eventu
		/// </summary>
		/// <param name="sender">Odesilatel události, typicky IHeaderSync, kde k události došlo</param>
		/// <param name="args">Data události</param>
		/// <param name="handler">Handler eventu - nahoru</param>
        /// <param name="item">Typ vlastnosti, ten se testuje zda se má/nemá synchronizovat</param>
		/// <param name="interactive">Interaktivní režim</param>
        private void _Sync_ColumnWidth(object sender, DColumnWidthEventArgs args, DColumnWidthEventHandler handler, DColumnsSynchronizedItems item, bool interactive)
		{
			if (_SyncInProgress) return;
			try
			{
				_SyncInProgress = true;

                if (_IsToSync(item))
                {
                    IHeaderSync source = sender as IHeaderSync;
                    if (source != null)
                    {
                        foreach (IHeaderSync target in this.SyncObjList.FindAll(c => !Object.ReferenceEquals(c, source)))
                            // Pro všechny IHeaderSync, které jsou jiné než source:
                            target.SychronizeColumnWidthFrom(source, args, interactive);
                    }
                }
				if (handler != null)
					handler(sender, args);
			}
			finally
			{
				_SyncInProgress = false;
			}
		}
        /// <summary>
        /// Synchronizuje viditelnost sloupců, po dobu synchronizace potlačuje rekurzivní volání téhož eventu
        /// </summary>
        /// <param name="sender">Odesilatel události, typicky IHeaderSync, kde k události došlo</param>
        /// <param name="args">Data události</param>
        /// <param name="handler">Handler eventu - nahoru</param>
        /// <param name="item">Typ vlastnosti, ten se testuje zda se má/nemá synchronizovat</param>
        /// <param name="interactive">Interaktivní režim</param>
        private void _Sync_ColumnVisible(object sender, DColumnEventArgs args, DColumnEventHandler handler, DColumnsSynchronizedItems item, bool interactive)
        {
            if (_SyncInProgress) return;
            try
            {
                _SyncInProgress = true;

                if (_IsToSync(item))
                {
                    IHeaderSync source = sender as IHeaderSync;
                    if (source != null)
                    {
                        foreach (IHeaderSync target in this.SyncObjList.FindAll(c => !Object.ReferenceEquals(c, source)))
                            // Pro všechny IHeaderSync, které jsou jiné než source:
                            target.SychronizeColumnVisibleFrom(source, args, interactive);
                    }
                }
                if (handler != null)
                    handler(sender, args);
            }
            finally
            {
                _SyncInProgress = false;
            }
        }
        /// <summary>
        /// Synchronizuje pořadí sloupců, po dobu synchronizace potlačuje rekurzivní volání téhož eventu
        /// </summary>
        /// <param name="sender">Odesilatel události, typicky IHeaderSync, kde k události došlo</param>
        /// <param name="args">Data události</param>
        /// <param name="handler">Handler eventu - nahoru</param>
        /// <param name="item">Typ vlastnosti, ten se testuje zda se má/nemá synchronizovat</param>
        /// <param name="interactive">Interaktivní režim</param>
        private void _Sync_ColumnOrder(object sender, DColumnEventArgs args, DColumnEventHandler handler, DColumnsSynchronizedItems item, bool interactive)
        {
            if (_SyncInProgress) return;
            try
            {
                _SyncInProgress = true;

                if (_IsToSync(item))
                {
                    IHeaderSync source = sender as IHeaderSync;
                    if (source != null)
                    {
                        foreach (IHeaderSync target in this.SyncObjList.FindAll(c => !Object.ReferenceEquals(c, source)))
                            // Pro všechny IHeaderSync, které jsou jiné než source:
                            target.SychronizeColumnOrderFrom(source, args, interactive);
                    }
                }
                if (handler != null)
                    handler(sender, args);
            }
            finally
            {
                _SyncInProgress = false;
            }
        }
        /// <summary>
        /// Synchronizuje třídění sloupců, po dobu synchronizace potlačuje rekurzivní volání téhož eventu
        /// </summary>
        /// <param name="sender">Odesilatel události, typicky IHeaderSync, kde k události došlo</param>
        /// <param name="args">Data události</param>
        /// <param name="handler">Handler eventu - nahoru</param>
        /// <param name="item">Typ vlastnosti, ten se testuje zda se má/nemá synchronizovat</param>
        /// <param name="interactive">Interaktivní režim</param>
        private void _Sync_ColumnSort(object sender, DColumnEventArgs args, DColumnEventHandler handler, DColumnsSynchronizedItems item, bool interactive)
        {
            if (_SyncInProgress) return;
            try
            {
                _SyncInProgress = true;

                if (_IsToSync(item))
                {
                    IHeaderSync source = sender as IHeaderSync;
                    if (source != null)
                    {
                        foreach (IHeaderSync target in this.SyncObjList.FindAll(c => !Object.ReferenceEquals(c, source)))
                            // Pro všechny IHeaderSync, které jsou jiné než source:
                            target.SychronizeColumnSortFrom(source, args, interactive);
                    }
                }
                if (handler != null)
                    handler(sender, args);
            }
            finally
            {
                _SyncInProgress = false;
            }
        }
        /// <summary>
		/// Synchronizuje TimeAxis., po dobu synchronizace potlačuje rekurzivní volání téhož eventu
		/// </summary>
		/// <param name="sender">Odesilatel události, typicky IHeaderSync, kde k události došlo</param>
		/// <param name="args">Data události</param>
		/// <param name="handler">Handler eventu - nahoru</param>
        /// <param name="item">Typ vlastnosti, ten se testuje zda se má/nemá synchronizovat</param>
        /// <param name="interactive">Interaktivní režim</param>
		private void _Sync_TimeAxisRange(object sender, TimeAxisChangeEventArgs args, TimeAxisChangeHandler handler, DColumnsSynchronizedItems item, bool interactive)
		{
			if (_SyncInProgress) return;
			try
			{
				_SyncInProgress = true;

                if (_IsToSync(item))
                {
                    IHeaderSync source = sender as IHeaderSync;
                    if (source != null)
                    {
                        foreach (IHeaderSync target in this.SyncObjList.FindAll(c => !Object.ReferenceEquals(c, source)))
                            // Pro všechny IHeaderSync, které jsou jiné než source:
                            target.SychronizeTimeAxisRangeFrom(source, args, interactive);
                    }
                }

				if (handler != null)
					handler(sender, args);
			}
			finally
			{
				_SyncInProgress = false;
			}
		}
		/// <summary>
		/// Příznak, že synchronizace je aktivní, další synchronizace by se neměla provádět.
		/// </summary>
		private bool _SyncInProgress;
        /// <summary>
        /// Vrací true, pokud daná vlastnost má být synchronizována (podle obsahu this._SynchronizedItems).
        /// </summary>
        /// <param name="sync">Dotazovaná vlastnost</param>
        /// <returns></returns>
        private bool _IsToSync(DColumnsSynchronizedItems sync)
        {
            return ((this._SynchronizedItems & sync) > 0);
        }
		#endregion
	}
	#endregion
    #region DELEGÁTI A EVENTARGS A ENUM
    /// <summary>
    /// Delegát, předepisující handler obecné události na sloupci
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="args"></param>
    public delegate void DColumnEventHandler(object sender, DColumnEventArgs args);
    /// <summary>
    /// Data předávaná při události na sloupci
    /// </summary>
    public class DColumnEventArgs : EventArgs
    {
        public DColumnEventArgs(DColumn column)
        {
            this.Column = column;
        }
        /// <summary>
        /// Sloupec, na němž došlo k události
        /// </summary>
        public DColumn Column { get; private set; }
    }
    /// <summary>
    /// Delegát, předepisující handler události při změně šířky sloupce na konkrétním sloupci
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="args"></param>
    public delegate void DColumnWidthEventHandler(object sender, DColumnWidthEventArgs args);
    /// <summary>
    /// Data předávaná při události při změně šířky sloupce na konkrétním sloupci
    /// </summary>
    public class DColumnWidthEventArgs  : EventArgs
    {
        public DColumnWidthEventArgs(DColumn column, List<int> columnWidthList)
        {
            this.Column = column;
            this.ColumnWidthList = columnWidthList;
        }
        /// <summary>
        /// Sloupec, na němž došlo k události
        /// </summary>
        public DColumn Column { get; private set; }
        /// <summary>
        /// Souhrn šířek všech sloupců v jejich nativním pořadí
        /// </summary>
        public List<int> ColumnWidthList { get; private set; }
    }
    /// <summary>
    /// Delegát, předepisující handler události při změně řádkového filtru na sloupci
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="args"></param>
    public delegate void DColumnRowFilterEventHandler(object sender, DColumnRowFilterEventArgs args);
    /// <summary>
    /// Data předávaná při změně řádkového filtru na sloupci
    /// </summary>
    public class DColumnRowFilterEventArgs : DColumnEventArgs
    {
        public DColumnRowFilterEventArgs(DColumn column, RowFilter rowFilter)
            : base(column)
        {
            this.Column = column;
            this.RowFilter = rowFilter;
        }
        /// <summary>
        /// Sloupec, na němž došlo k události
        /// </summary>
        public DColumn Column { get; private set; }
        /// <summary>
        /// Řádkový filtr platný v okamžiku události
        /// </summary>
        public RowFilter RowFilter { get; private set; }
    }
    /// <summary>
    /// Vlastnosti, pro které synchronizer zajišťuje jejich synchronizaci mezi IHeaderSync. Hodnoty lze sčítat.
    /// </summary>
    [Flags]
    public enum DColumnsSynchronizedItems
    {
        /// <summary>
        /// Nic se nebude synchronizovat
        /// </summary>
        None = 0,
        /// <summary>
        /// Synchronizovat změnu šířky v průběhu její změny (interaktivně)
        /// </summary>
        ColumnWidthChanging = 1,
        /// <summary>
        /// Synchronizovat změnu šířky po jejím dokončení
        /// </summary>
        ColumnWidthChanged = ColumnWidthChanging << 1,
        /// <summary>
        /// Synchronizovat změnu výšky záhlaví v průběhu její změny (interaktivně)
        /// </summary>
        ColumnHeaderHeightChanging = ColumnWidthChanged << 1,
        /// <summary>
        /// Synchronizovat změnu výšky záhlaví po jejím dokončení
        /// </summary>
        ColumnHeaderHeightChanged = ColumnHeaderHeightChanging << 1,
        /// <summary>
        /// Synchronizovat změnu Visible sloupců
        /// </summary>
        ColumnVisibleChanged = ColumnHeaderHeightChanged << 1,
        /// <summary>
        /// Synchronizovat změnu pořadí sloupců
        /// </summary>
        ColumnOrderChanged = ColumnVisibleChanged << 1,
        /// <summary>
        /// Synchronizovat třídění dat podle sloupce
        /// </summary>
        ColumnSortChanged = ColumnOrderChanged << 1,
        /// <summary>
        /// Synchronizovat změnu časové osy
        /// </summary>
        TimeAxisRangeChanged = ColumnSortChanged << 1
    }
    /// <summary>
    /// Druh pohyblivosti hlavičky ve směru vertikálním
    /// </summary>
    public enum DColumnsHeaderMoveMode
    {
        /// <summary>
        /// Hlavička se nepohybuje
        /// </summary>
        None = 1,
        /// <summary>
        /// Hlavička se pohybuje v rámci svého parenta, parent zůstává na místě.
        /// Režim vhodný pro samostatnou hlavičku, umístěnou volně na podkladovém controlu.
        /// </summary>
        MoveInParent,
        /// <summary>
        /// Hlavička zůstává stát na stejném místě parenta (souřadnice Top), ale přesouvá se souřadnice Top tohoto parenta a přiměřeně se mění jeho Height.
        /// Režim je vhodný pro hlavičku, která je umístěna na podkladu společně s níže umístěným listem/tree, a posouvání hlavičky nahoru/dolů má provádět resize celého parenta (jako splitter).
        /// </summary>
        ResizeParent

    }
    #endregion
    #region INTERFACE IHeaderSync
    /// <summary>
    /// Interface, se kterým pracuje synchronizer DColumnsSynchronizer.
    /// Umožňuje, aby zdrojem a cílem synchronizace byl objekt buď hlavička, anebo komponenta obsahující hlavičku (s tím, že komponenta zajistí průhlednost na hlavičku).
    /// </summary>
    public interface IHeaderSync
    {
        List<TimeAxisTickInfo> AxisTicks { get; }
        TimeRange CurrentTimeRange { get; set; }
        ITimeAxisCalc TimeAxisCalc { get; }
        int ColumnHeaderHeightInteractive { get; }
        int ColumnHeaderHeight { get; }
        List<DColumn> Columns { get; }
        int ColumnsCount { get; }

        event EventHandler ColumnHeaderHeightChanging;
        event EventHandler ColumnHeaderHeightChanged;
        event DColumnWidthEventHandler ColumnWidthChanging;
        event DColumnWidthEventHandler ColumnWidthChanged;
        event DColumnEventHandler ColumnVisibleChanged;
        event TimeAxisChangeHandler TimeAxisChange;
        event DColumnEventHandler ColumnSortChanged;
        event DColumnRowFilterEventHandler RowFilterChanged;

        void SychronizeColumnHeightFrom(IHeaderSync source, EventArgs args, bool interactive);
        void SychronizeColumnWidthFrom(IHeaderSync source, DColumnWidthEventArgs args, bool interactive);
        void SychronizeColumnVisibleFrom(IHeaderSync source, DColumnEventArgs args, bool interactive);
        void SychronizeColumnOrderFrom(IHeaderSync source, DColumnEventArgs args, bool interactive);
        void SychronizeColumnSortFrom(IHeaderSync source, DColumnEventArgs args, bool interactive);
        void SychronizeTimeAxisRangeFrom(IHeaderSync source, TimeAxisChangeEventArgs args, bool interactive);

        void SuppressDrawingPush();
        void SuppressDrawingPop();
        bool SuppressDrawing { get; set; }
        void SetTimeAxisData(TimeAxisSetAxisArgs e);
        void StopTimeAxisMove();
    }
	#endregion
}
